Skip to content

Instantly share code, notes, and snippets.

@TekWizely
Created October 29, 2021 23:36
Show Gist options
  • Save TekWizely/f14c787fd7ecf7719d2963265a6e7d08 to your computer and use it in GitHub Desktop.
Save TekWizely/f14c787fd7ecf7719d2963265a6e7d08 to your computer and use it in GitHub Desktop.
Bash script to parse the output of 'update-alternatives --query'
# shellcheck shell=bash
#######################################################################
# SPDX-License-Identifier: MIT
# Copyright (c) 2021 TekWizely & co-authors
#
# Use of this source code is governed by the MIT license.
# See the accompanying LICENSE file, if present, or visit:
# https://opensource.org/licenses/MIT
#######################################################################
# VERSION="v1.0.0"
#######################################################################
# alt_parse_query - Parses the output of 'update-alternatives --query'
#
# $1 = file to read from (can be process sub)
# $2 = debug|trace
#
# example:
#
# alt_parse_query <( update-alternatives --query pager )
#
function alt_parse_query() {
# Reset global vars
#
ALT_GROUP_LINK=
ALT_GROUP_NAME=
ALT_GROUP_SLAVE_NAMES=()
ALT_GROUP_SLAVE_LINKS=()
ALT_GROUP_STATUS= # auto | manual
ALT_GROUP_CURRENT_VALUE=
ALT_GROUP_CURRENT_INDEX=
ALT_GROUP_BEST_VALUE=
ALT_GROUP_BEST_INDEX=
ALT_GROUP_VALUES=()
ALT_GROUP_PRIORITIES=()
# TODO Consider moving to serialized arrays for slave values
#ALT_GROUP_VALUE_${v}_SLAVE_${s}_VALUE
_ALT_PARSE_QUERY_STATE="NAME"
local IFS line lineno=0 # 1 on first use
while IFS="" read -r line || [ -n "${line}" ]; do
((lineno += 1))
if [ "${2:-}" == "trace" ]; then
printf "# line NO : %s\n" "${lineno}"
printf "# line TEXT: '%s'\n" "${line}"
printf "# state : %s\n" "${_ALT_PARSE_QUERY_STATE}"
printf "# ---------\n"
fi
# Returns on !0 return code from state functions
#
_alt_parse_query_state_"${_ALT_PARSE_QUERY_STATE}" "${line}" || return $?
done <"${1}"
# Final state should be one of "eof candidate" states
#
if [ ! "${_ALT_PARSE_QUERY_STATE}" = "MAYBE_ALT_START" ] && [ ! "${_ALT_PARSE_QUERY_STATE}" = "MAYBE_ALT_SLAVES_HEADER" ] && [ ! "${_ALT_PARSE_QUERY_STATE}" = "MAYBE_ALT_SLAVE_ENTRY" ]; then
printf "unexpected parse state: %s\n" "${_ALT_PARSE_QUERY_STATE}" >&2
return 2
fi
if [ "${2:-}" == "trace" ] || [ "${2:-}" == "debug" ]; then
printf "ALT_GROUP_NAME : %s\n" "${ALT_GROUP_NAME}"
printf "ALT_GROUP_LINK : %s\n" "${ALT_GROUP_LINK}"
printf "ALT_GROUP_SLAVE_NAMES : %s\n" "${ALT_GROUP_SLAVE_NAMES[*]}"
printf "ALT_GROUP_SLAVE_LINKS : %s\n" "${ALT_GROUP_SLAVE_LINKS[*]}"
printf "ALT_GROUP_STATUS : %s\n" "${ALT_GROUP_STATUS}"
printf "ALT_GROUP_CURRENT_VALUE: %s\n" "${ALT_GROUP_CURRENT_VALUE}"
printf "ALT_GROUP_CURRENT_INDEX: %s\n" "${ALT_GROUP_CURRENT_INDEX}"
printf "ALT_GROUP_BEST_VALUE : %s\n" "${ALT_GROUP_BEST_VALUE}"
printf "ALT_GROUP_BEST_INDEX : %s\n" "${ALT_GROUP_BEST_INDEX}"
printf "ALT_GROUP_VALUES : %s\n" "${ALT_GROUP_VALUES[*]}"
printf "ALT_GROUP_PRIORITIES : %s\n" "${ALT_GROUP_PRIORITIES[*]}"
local v=0
while [[ $v -lt ${#ALT_GROUP_VALUES[@]} ]]; do
local s=0
while [[ $s -lt ${#ALT_GROUP_SLAVE_NAMES[@]} ]]; do
local alt_slave_value_ref="ALT_GROUP_VALUE_${v}_SLAVE_${s}_VALUE"
printf "${alt_slave_value_ref} : %s\n" "${!alt_slave_value_ref}"
((s += 1))
done
((v += 1))
done
fi
}
function _alt_parse_query_state_NAME() {
local regex="^Name: (.+)$"
if [[ "${1}" =~ $regex ]]; then
ALT_GROUP_NAME="${BASH_REMATCH[1]}"
_ALT_PARSE_QUERY_STATE="LINK"
else
printf "unable to parse Name field: %s\n" "${1}" >&2
return 2
fi
}
function _alt_parse_query_state_LINK() {
local regex="^Link: (.+)$"
if [[ "${1}" =~ $regex ]]; then
ALT_GROUP_LINK="${BASH_REMATCH[1]}"
_ALT_PARSE_QUERY_STATE="MAYBE_SLAVES_HEADER"
else
printf "unable to parse Link field: %s\n" "${1}" >&2
return 2
fi
}
function _alt_parse_query_state_MAYBE_SLAVES_HEADER() {
if [[ "${1}" =~ ^Slaves:$ ]]; then
_ALT_PARSE_QUERY_STATE="SLAVE_ENTRY"
else
_ALT_PARSE_QUERY_STATE="STATUS"
_alt_parse_query_state_"${_ALT_PARSE_QUERY_STATE}" "${1}"
fi
}
function _alt_parse_query_state_SLAVE_ENTRY() {
local regex="^ ([^ ]+) (.+)$"
if [[ "${1}" =~ $regex ]]; then
ALT_GROUP_SLAVE_NAMES+=("${BASH_REMATCH[1]}")
ALT_GROUP_SLAVE_LINKS+=("${BASH_REMATCH[2]}")
# Leave status unchanged
else
_ALT_PARSE_QUERY_STATE="STATUS"
_alt_parse_query_state_"${_ALT_PARSE_QUERY_STATE}" "${1}"
fi
}
function _alt_parse_query_state_STATUS() {
local regex="^Status: (.+)$"
if [[ "${1}" =~ $regex ]]; then
ALT_GROUP_STATUS="${BASH_REMATCH[1]}"
_ALT_PARSE_QUERY_STATE="BEST"
else
printf "unable to parse Status field: %s\n" "${1}" >&2
return 2
fi
}
function _alt_parse_query_state_BEST() {
local regex="^Best: (.+)$"
if [[ "${1}" =~ $regex ]]; then
ALT_GROUP_BEST_VALUE="${BASH_REMATCH[1]}"
_ALT_PARSE_QUERY_STATE="VALUE"
else
printf "unable to parse Best field: %s\n" "${1}" >&2
return 2
fi
}
function _alt_parse_query_state_VALUE() {
local regex="^Value: (.+)$"
if [[ "${1}" =~ $regex ]]; then
ALT_GROUP_CURRENT_VALUE="${BASH_REMATCH[1]}"
_ALT_PARSE_QUERY_STATE="MAYBE_ALT_START"
else
printf "unable to parse Value field: %s\n" "${1}" >&2
return 2
fi
}
# eof candidate
function _alt_parse_query_state_MAYBE_ALT_START() {
if [[ "${1}" != "" ]]; then
printf "expecting blank line to start an alternative entry: %s\n" "${1}" >&2
return 2
fi
_ALT_PARSE_QUERY_STATE="ALT_VALUE"
}
function _alt_parse_query_state_ALT_VALUE() {
local regex="^Alternative: (.+)$"
if [[ "${1}" =~ $regex ]]; then
local alt_value="${BASH_REMATCH[1]}"
local alt_index=${#ALT_GROUP_VALUES[@]} # assign before += for 0-based value
ALT_GROUP_VALUES+=("${alt_value}")
# best alt index
#
if [ "${alt_value}" == "${ALT_GROUP_BEST_VALUE}" ]; then
ALT_GROUP_BEST_INDEX=$alt_index
fi
# current alt index
#
if [ "${alt_value}" == "${ALT_GROUP_CURRENT_VALUE}" ]; then
ALT_GROUP_CURRENT_INDEX=$alt_index
fi
_ALT_PARSE_QUERY_STATE="ALT_PRIORITY"
else
printf "unable to parse Alternative value field: %s\n" "${1}" >&2
return 2
fi
}
function _alt_parse_query_state_ALT_PRIORITY() {
local regex="^Priority: ([0-9]+)$"
if [[ "${1}" =~ $regex ]]; then
ALT_GROUP_PRIORITIES+=("${BASH_REMATCH[1]}")
_ALT_PARSE_QUERY_STATE="MAYBE_ALT_SLAVES_HEADER"
else
printf "unable to parse alternative Priority field: %s\n" "${1}" >&2
return 2
fi
}
# eof candidate
function _alt_parse_query_state_MAYBE_ALT_SLAVES_HEADER() {
if [[ "${1}" =~ ^Slaves:$ ]]; then
_ALT_PARSE_QUERY_STATE="MAYBE_ALT_SLAVE_ENTRY"
else
_ALT_PARSE_QUERY_STATE="MAYBE_ALT_START"
_alt_parse_query_state_"${_ALT_PARSE_QUERY_STATE}" "${1}"
fi
}
# eof candidate
function _alt_parse_query_state_MAYBE_ALT_SLAVE_ENTRY() {
local regex="^ ([^ ]+) (.+)$"
if [[ "${1}" =~ $regex ]]; then
local slave_name="${BASH_REMATCH[1]}"
local slave_value="${BASH_REMATCH[2]}"
local slave_name_index=
local i=0
while [[ $i -lt ${#ALT_GROUP_SLAVE_NAMES[@]} ]]; do
if [ "${ALT_GROUP_SLAVE_NAMES[$i]}" == "${slave_name}" ]; then
slave_name_index=$i
break
fi
i=$((i + 1))
done
# find it?
if [ -n "${slave_name_index}" ]; then
local alt_index=$((${#ALT_GROUP_VALUES[@]} - 1))
local slave_value_ref="ALT_GROUP_VALUE_${alt_index}_SLAVE_${slave_name_index}_VALUE"
declare -g "${slave_value_ref}"="${slave_value}"
#else
# printf "unable to match alternative slave Name: %s - skipping\n" "${1}" >&2
fi
# Leave status unchanged
else
_ALT_PARSE_QUERY_STATE="MAYBE_ALT_START"
_alt_parse_query_state_"${_ALT_PARSE_QUERY_STATE}" "${1}"
fi
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment