home · contact · privacy
Add improved apt_get_digested.
authorChristian Heller <c.heller@plomlompom.de>
Sun, 17 Aug 2025 12:51:28 +0000 (14:51 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Sun, 17 Aug 2025 12:51:28 +0000 (14:51 +0200)
bookworm/scripts/lib/apt_get_digested.sh
trixie/scripts/lib/apt_get_digested.sh [changed from symlink to file mode: 0644]

index e9ac33fb20e77cd4b2d0acdef641defb885f608e..df4baa788c80c5f99c7996abdfa62380cecc8112 100644 (file)
-. 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
 }
deleted file mode 120000 (symlink)
index f79799352fe31230cf86a88cc18774ae740b6c43..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1 +0,0 @@
-../../../bookworm/scripts/lib/apt_get_digested.sh
\ No newline at end of file
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..df4baa788c80c5f99c7996abdfa62380cecc8112
--- /dev/null
@@ -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
+}