From: Christian Heller Date: Sun, 17 Aug 2025 12:51:28 +0000 (+0200) Subject: Add improved apt_get_digested. X-Git-Url: https://plomlompom.com/repos/blog?a=commitdiff_plain;h=ebd3a5743e757d59d336433a3d8d9eb38b0f628d;p=config Add improved apt_get_digested. --- diff --git a/bookworm/scripts/lib/apt_get_digested.sh b/bookworm/scripts/lib/apt_get_digested.sh index e9ac33f..df4baa7 100644 --- a/bookworm/scripts/lib/apt_get_digested.sh +++ b/bookworm/scripts/lib/apt_get_digested.sh @@ -1,92 +1,205 @@ -. lib/for_line_in.sh . lib/prefixed_msg.sh -apt_get_digested() { -prefixed_msg_init 'apt_get_digested' +# NB: to avoid confusing the IFS handling, ensure none of the lines is empty – +# even if ignored! +apt_get_digested_SEDS=$(cat << "DELIMITER" +Get: +Get:[[:digit:]]+ (.*) +\1 +-------------------- +Hit: +Hit:[[:digit:]]+ (.*) +\1 +-------------------- +. +Generation complete. +IGNORE +-------------------- +. +Preconfiguring packages \.\.\. +IGNORE +-------------------- +. +Preparing to unpack [[:print:]]+ \.\.\. +IGNORE +-------------------- +. +Unpacking [[:print:]]+ \([[:print:]]+\)( over \([[:print:]]+\))? \.\.\. +IGNORE +-------------------- +Adding diversion(s): +Adding 'diversion of ([[:print:]]+) to ([[:print:]]+) by ([[:print:]]+)' + \3:[\1 → \2] +-------------------- +Created symlink(s): +Created symlink '([[:print:]]+)' → '([[:print:]]+)'\. + [\1 → \2] +-------------------- +Installing new version of config file(s): +Installing new version of config file ([[:print:]]+) \.\.\. +\1 +-------------------- +Processing triggers for: +Processing triggers for ([[:print:]]+) \([[:print:]]+\) \.\.\. +\1 +-------------------- +Purging configuration files: +Purging configuration files for ([[:print:]]+) \([[:print:]]+\) \.\.\. +\1 +-------------------- +Removing: +Removing ([[:print:]]+) \([[:print:]]+\) \.\.\. +\1 +-------------------- +Removing diversion(s): +Removing 'diversion of ([[:print:]]+) to ([[:print:]]+) by ([[:print:]]+) + \3:[\1 → \2] +-------------------- +Removing obsolete conffile(s): +Removing obsolete conffile ([[:print:]]+) \.\.\. +\1 +-------------------- +Selecting previously unselected: +Selecting previously unselected package ([[:print:]]+)\. +\1 +-------------------- +Setting up: +Setting up ([[:print:]]+) \([[:print:]]+\) \.\.\. +\1 +-------------------- +Updating to current default: +Updating ([[:print:]]+) to current default. +\1 +-------------------- +disabled or static unit, so not starting: +([[:print:]]+) is a disabled or a static unit, not starting it\. +\1 +-------------------- -local FINISH_LINE='apt_get_digested_FINISH_LINE' -local SEDS=\ -'|\(Reading database \.\.\. .*|IGNORE -|Generation complete.|IGNORE -|Preconfiguring packages \.\.\.|IGNORE -|Preparing to unpack [[:print:]]+ \.\.\.|IGNORE -|Unpacking [[:print:]]+ \([[:print:]]+\)( over \([[:print:]]+\))? \.\.\.|IGNORE -Adding diversion(s):|Adding '"'"'diversion of ([[:print:]]+) to ([[:print:]]+) by ([[:print:]]+)'"'"'| \3:[\1 → \2] -Created symlink(s):|Created symlink '"'"'([[:print:]]+)'"'"' → '"'"'([[:print:]]+)'"'"'\.| [\1 → \2] -FOLLOW_PREFIX|([[:print:]]+) depends on ([[:print:]]+)( \([>=]+ [[:print:]]+\))?\.|[\2 → \1] -Installing new version of config file(s):|Installing new version of config file ([[:print:]]+) \.\.\.|\1 -Processing triggers for:|Processing triggers for ([[:print:]]+) \([[:print:]]+\) \.\.\.|\1 -Purging configuration files:|Purging configuration files for ([[:print:]]+) \([[:print:]]+\) \.\.\.|\1 -Removing:|Removing ([[:print:]]+) \([[:print:]]+\) \.\.\.|\1 -Removing diversion(s):|Removing '"'"'diversion of ([[:print:]]+) to ([[:print:]]+) by ([[:print:]]+)| \3:[\1 → \2] -Removing obsolete conffile(s):|Removing obsolete conffile ([[:print:]]+) \.\.\.|\1 -Selecting previously unselected:|Selecting previously unselected package ([[:print:]]+)\.|\1 -Setting up:|Setting up ([[:print:]]+) \([[:print:]]+\) \.\.\.|\1 -Updating to current default:|Updating ([[:print:]]+) to current default.|\1 -disabled or static unit, so not starting:|([[:print:]]+) is a disabled or a static unit, not starting it\.|\1 -disabled or static unit not running, so not starting:|([[:print:]]+) is a disabled or a static unit not running, not starting it\.|\1 -BECOME_NEXT_PREFIX|dpkg: ([[:print:]]+): dependency problems, but removing anyway as you requested:|dpkg warns: removing \1 despite dependency problems: -dpkg warns: unable to delete old directories (not empty):|dpkg: warning: unable to delete old directory '"'"'([[:print:]]+)'"'"': Directory not empty|\1 -update-alternatives: providing in auto mode:|update-alternatives: using ([[:print:]]+) to provide ([[:print:]]+) \(([[:print:]]+)\) in auto mode| [\1 → \2 (\3)]' +disabled or static unit not running, so not starting: +([[:print:]]+) is a disabled or a static unit not running, not starting it\. +\1 +-------------------- +dpkg warns: unable to delete old directories (not empty): +dpkg: warning: unable to delete old directory '"'"'([[:print:]]+)'"'"': Directory not empty +\1 +-------------------- +update-alternatives: providing in auto mode: +update-alternatives: using ([[:print:]]+) to provide ([[:print:]]+) \(([[:print:]]+)\) in auto mode + [\1 → \2 (\3)] +-------------------- +Warning: Problem unlinking files (Remove Caches/13: Permission denied): +Warning: Problem unlinking the file ([[:print:]]+) - RemoveCaches \(13: Permission denied\) +\1 +-------------------- +. +\(Reading database \.\.\. .* +IGNORE +-------------------- +. +WARNING: apt does not have a stable CLI interface.* +IGNORE +-------------------- +Warning: Problem unlinking files (Remove Caches/13: Permission denied): +Warning: Problem unlinking the file ([[:print:]]+) - RemoveCaches \(13: Permission denied\) +\1 +-------------------- +FOLLOW_PREFIX +([[:print:]]+) depends on ([[:print:]]+)( \([>=]+ [[:print:]]+\))?\. +[\2 → \1] +-------------------- +BECOME_NEXT_PREFIX +dpkg: ([[:print:]]+): dependency problems, but removing anyway as you requested: +dpkg warns: removing \1 despite dependency problems: +-------------------- +DELIMITER +) -print_nl_if_started_printing() { - if [ -n "${STARTED_PRINTING}" ]; then - printf '\n' - fi - printf '\r' -} -local LAST_PREFIX='' -local STARTED_PRINTING= -local LEADER_PREFIX= -local APT_GET_EXIT_CODE= -local APT_LINE= -eval "set +e; DEBIAN_FRONTEND=noninteractive apt install -y $1; APT_GET_EXIT_CODE=\$?; set -e; echo ${FINISH_LINE} \${APT_GET_EXIT_CODE}" 2>&1 | while read APT_LINE; do - APT_LINE=$(printf '%s' "${APT_LINE}" | tr -d '\r') - if [ -z "${APT_LINE}" ]; then - continue - elif [ "$(echo ${APT_LINE} | cut -d' ' -f1)" = "${FINISH_LINE}" ]; then - print_nl_if_started_printing - local EXIT_CODE=$(echo ${APT_LINE} | cut -d' ' -f2) - if [ "${EXIT_CODE}" != "0" ]; then - false - fi - break - fi - local COMPRESSED_ITEM='' - local PREFIX= - try_seds() { - local SED=$1 - PREFIX=$(printf '%s' "${SED}" | cut -d'|' -f1) - local PATTERN=$(printf '^%s$' "${SED}" | cut -d'|' -f2) - local REPLACEMENT=$(printf '%s' "${SED}" | cut -d'|' -f3) - if printf '%s' "${APT_LINE}" | grep -E "${PATTERN}" > /dev/null; then - COMPRESSED_ITEM=$(printf '%s' "${APT_LINE}" | sed -E "s|${PATTERN}|${REPLACEMENT}|") - if [ "${PREFIX}" = "BECOME_NEXT_PREFIX" ]; then - LEADER_PREFIX="${COMPRESSED_ITEM}" - COMPRESSED_ITEM=IGNORE - elif [ "${PREFIX}" = "FOLLOW_PREFIX" ]; then - PREFIX="${LEADER_PREFIX}" +apt_get_digested() { + prefixed_msg_init 'apt_get_digested' + local APT_GET_EXIT_CODE + local FINISH_LINE='apt_get_digested_FINISH_LINE' + + run_apt() { + set +e + DEBIAN_FRONTEND=noninteractive apt -y "$@" + APT_GET_EXIT_CODE=$? + set -e + echo "${FINISH_LINE} ${APT_GET_EXIT_CODE}" + } + + local OUR_NEWLINE='\r' # be \n\r except on 1st run, so no empty 1st line + local LAST_PREFIX= + local LEADER_PREFIX + local APT_LINE + run_apt "$@" 2>&1 | while read -r APT_LINE; do + APT_LINE=$(printf '%s' "${APT_LINE}" | tr -d '\r') + if [ -z "${APT_LINE}" ]; then + continue + else + local FIRST_FIELD + FIRST_FIELD=$(echo "${APT_LINE}" | cut -d' ' -f1) + if [ "${FIRST_FIELD}" = "${FINISH_LINE}" ]; then + printf '%s' "${OUR_NEWLINE}" + local EXIT_CODE + EXIT_CODE=$(echo "${APT_LINE}" | cut -d' ' -f2) + if [ "${EXIT_CODE}" != "0" ]; then + false + fi + break fi - break fi - } - for_line_in "${SEDS}" try_seds - if [ "${COMPRESSED_ITEM}" = "IGNORE" ]; then - continue - elif [ -z "${COMPRESSED_ITEM}" ]; then - print_nl_if_started_printing - prefixed_msg_no_nl "${APT_LINE}" - LAST_PREFIX='' - else - if [ "${PREFIX}" != "${LAST_PREFIX}" ]; then - print_nl_if_started_printing - prefixed_msg_no_nl "# ${PREFIX}" - LAST_PREFIX="${PREFIX}" + local COMPRESSED_ITEM= + local PREFIX + local PATTERN + local REPLACEMENT + local I=0 + local LINE + local OLD_IFS=${IFS} + local IFS + IFS=$(printf '\n\r') + for LINE in ${apt_get_digested_SEDS}; do + I=$((1 + "$I")) + if [ "$I" -eq 1 ]; then + PREFIX="${LINE}" + continue + elif [ "$I" -eq 2 ]; then + PATTERN="${LINE}" + continue + elif [ "$I" -eq 3 ]; then + REPLACEMENT="${LINE}" + continue + elif [ "$I" -eq 4 ]; then + I=0 + fi + if printf '%s' "${APT_LINE}" | grep -E "${PATTERN}" > /dev/null; then + COMPRESSED_ITEM=$(printf '%s' "${APT_LINE}" | sed -E "s|${PATTERN}|${REPLACEMENT}|") + #if [ "${PREFIX}" = "BECOME_NEXT_PREFIX" ]; then + # LEADER_PREFIX="${COMPRESSED_ITEM}" + # COMPRESSED_ITEM=IGNORE + #elif [ "${PREFIX}" = "FOLLOW_PREFIX" ]; then + # PREFIX="${LEADER_PREFIX}" + #fi + break + fi + done + IFS=${OLD_IFS} + if [ "${COMPRESSED_ITEM}" = 'IGNORE' ]; then + continue + elif [ -z "${COMPRESSED_ITEM}" ]; then + printf '%s' "${OUR_NEWLINE}" + prefixed_msg_no_nl "${APT_LINE}" + LAST_PREFIX= + else + if [ "${PREFIX}" != "${LAST_PREFIX}" ]; then + printf '%s' "${OUR_NEWLINE}" + prefixed_msg_no_nl "# ${PREFIX}" + LAST_PREFIX="${PREFIX}" + fi + printf ' %s' "${COMPRESSED_ITEM}" fi - printf ' %s' "${COMPRESSED_ITEM}" - fi - STARTED_PRINTING=1 -done - -prefixed_msg_exit + OUR_NEWLINE='\n\r' + done + + prefixed_msg_exit } diff --git a/trixie/scripts/lib/apt_get_digested.sh b/trixie/scripts/lib/apt_get_digested.sh deleted file mode 120000 index f797993..0000000 --- a/trixie/scripts/lib/apt_get_digested.sh +++ /dev/null @@ -1 +0,0 @@ -../../../bookworm/scripts/lib/apt_get_digested.sh \ No newline at end of file diff --git a/trixie/scripts/lib/apt_get_digested.sh b/trixie/scripts/lib/apt_get_digested.sh new file mode 100644 index 0000000..df4baa7 --- /dev/null +++ b/trixie/scripts/lib/apt_get_digested.sh @@ -0,0 +1,205 @@ +. lib/prefixed_msg.sh + +# NB: to avoid confusing the IFS handling, ensure none of the lines is empty – +# even if ignored! +apt_get_digested_SEDS=$(cat << "DELIMITER" +Get: +Get:[[:digit:]]+ (.*) +\1 +-------------------- +Hit: +Hit:[[:digit:]]+ (.*) +\1 +-------------------- +. +Generation complete. +IGNORE +-------------------- +. +Preconfiguring packages \.\.\. +IGNORE +-------------------- +. +Preparing to unpack [[:print:]]+ \.\.\. +IGNORE +-------------------- +. +Unpacking [[:print:]]+ \([[:print:]]+\)( over \([[:print:]]+\))? \.\.\. +IGNORE +-------------------- +Adding diversion(s): +Adding 'diversion of ([[:print:]]+) to ([[:print:]]+) by ([[:print:]]+)' + \3:[\1 → \2] +-------------------- +Created symlink(s): +Created symlink '([[:print:]]+)' → '([[:print:]]+)'\. + [\1 → \2] +-------------------- +Installing new version of config file(s): +Installing new version of config file ([[:print:]]+) \.\.\. +\1 +-------------------- +Processing triggers for: +Processing triggers for ([[:print:]]+) \([[:print:]]+\) \.\.\. +\1 +-------------------- +Purging configuration files: +Purging configuration files for ([[:print:]]+) \([[:print:]]+\) \.\.\. +\1 +-------------------- +Removing: +Removing ([[:print:]]+) \([[:print:]]+\) \.\.\. +\1 +-------------------- +Removing diversion(s): +Removing 'diversion of ([[:print:]]+) to ([[:print:]]+) by ([[:print:]]+) + \3:[\1 → \2] +-------------------- +Removing obsolete conffile(s): +Removing obsolete conffile ([[:print:]]+) \.\.\. +\1 +-------------------- +Selecting previously unselected: +Selecting previously unselected package ([[:print:]]+)\. +\1 +-------------------- +Setting up: +Setting up ([[:print:]]+) \([[:print:]]+\) \.\.\. +\1 +-------------------- +Updating to current default: +Updating ([[:print:]]+) to current default. +\1 +-------------------- +disabled or static unit, so not starting: +([[:print:]]+) is a disabled or a static unit, not starting it\. +\1 +-------------------- + +disabled or static unit not running, so not starting: +([[:print:]]+) is a disabled or a static unit not running, not starting it\. +\1 +-------------------- +dpkg warns: unable to delete old directories (not empty): +dpkg: warning: unable to delete old directory '"'"'([[:print:]]+)'"'"': Directory not empty +\1 +-------------------- +update-alternatives: providing in auto mode: +update-alternatives: using ([[:print:]]+) to provide ([[:print:]]+) \(([[:print:]]+)\) in auto mode + [\1 → \2 (\3)] +-------------------- +Warning: Problem unlinking files (Remove Caches/13: Permission denied): +Warning: Problem unlinking the file ([[:print:]]+) - RemoveCaches \(13: Permission denied\) +\1 +-------------------- +. +\(Reading database \.\.\. .* +IGNORE +-------------------- +. +WARNING: apt does not have a stable CLI interface.* +IGNORE +-------------------- +Warning: Problem unlinking files (Remove Caches/13: Permission denied): +Warning: Problem unlinking the file ([[:print:]]+) - RemoveCaches \(13: Permission denied\) +\1 +-------------------- +FOLLOW_PREFIX +([[:print:]]+) depends on ([[:print:]]+)( \([>=]+ [[:print:]]+\))?\. +[\2 → \1] +-------------------- +BECOME_NEXT_PREFIX +dpkg: ([[:print:]]+): dependency problems, but removing anyway as you requested: +dpkg warns: removing \1 despite dependency problems: +-------------------- +DELIMITER +) + +apt_get_digested() { + prefixed_msg_init 'apt_get_digested' + local APT_GET_EXIT_CODE + local FINISH_LINE='apt_get_digested_FINISH_LINE' + + run_apt() { + set +e + DEBIAN_FRONTEND=noninteractive apt -y "$@" + APT_GET_EXIT_CODE=$? + set -e + echo "${FINISH_LINE} ${APT_GET_EXIT_CODE}" + } + + local OUR_NEWLINE='\r' # be \n\r except on 1st run, so no empty 1st line + local LAST_PREFIX= + local LEADER_PREFIX + local APT_LINE + run_apt "$@" 2>&1 | while read -r APT_LINE; do + APT_LINE=$(printf '%s' "${APT_LINE}" | tr -d '\r') + if [ -z "${APT_LINE}" ]; then + continue + else + local FIRST_FIELD + FIRST_FIELD=$(echo "${APT_LINE}" | cut -d' ' -f1) + if [ "${FIRST_FIELD}" = "${FINISH_LINE}" ]; then + printf '%s' "${OUR_NEWLINE}" + local EXIT_CODE + EXIT_CODE=$(echo "${APT_LINE}" | cut -d' ' -f2) + if [ "${EXIT_CODE}" != "0" ]; then + false + fi + break + fi + fi + local COMPRESSED_ITEM= + local PREFIX + local PATTERN + local REPLACEMENT + local I=0 + local LINE + local OLD_IFS=${IFS} + local IFS + IFS=$(printf '\n\r') + for LINE in ${apt_get_digested_SEDS}; do + I=$((1 + "$I")) + if [ "$I" -eq 1 ]; then + PREFIX="${LINE}" + continue + elif [ "$I" -eq 2 ]; then + PATTERN="${LINE}" + continue + elif [ "$I" -eq 3 ]; then + REPLACEMENT="${LINE}" + continue + elif [ "$I" -eq 4 ]; then + I=0 + fi + if printf '%s' "${APT_LINE}" | grep -E "${PATTERN}" > /dev/null; then + COMPRESSED_ITEM=$(printf '%s' "${APT_LINE}" | sed -E "s|${PATTERN}|${REPLACEMENT}|") + #if [ "${PREFIX}" = "BECOME_NEXT_PREFIX" ]; then + # LEADER_PREFIX="${COMPRESSED_ITEM}" + # COMPRESSED_ITEM=IGNORE + #elif [ "${PREFIX}" = "FOLLOW_PREFIX" ]; then + # PREFIX="${LEADER_PREFIX}" + #fi + break + fi + done + IFS=${OLD_IFS} + if [ "${COMPRESSED_ITEM}" = 'IGNORE' ]; then + continue + elif [ -z "${COMPRESSED_ITEM}" ]; then + printf '%s' "${OUR_NEWLINE}" + prefixed_msg_no_nl "${APT_LINE}" + LAST_PREFIX= + else + if [ "${PREFIX}" != "${LAST_PREFIX}" ]; then + printf '%s' "${OUR_NEWLINE}" + prefixed_msg_no_nl "# ${PREFIX}" + LAST_PREFIX="${PREFIX}" + fi + printf ' %s' "${COMPRESSED_ITEM}" + fi + OUR_NEWLINE='\n\r' + done + + prefixed_msg_exit +}