Skip to content

Instantly share code, notes, and snippets.

@ThinGuy
Created June 20, 2025 22:30
Show Gist options
  • Select an option

  • Save ThinGuy/d34e43d650b55425522d4534886e3ddb to your computer and use it in GitHub Desktop.

Select an option

Save ThinGuy/d34e43d650b55425522d4534886e3ddb to your computer and use it in GitHub Desktop.
An Ubuntu Package Counter - Counts all vs installed and creates manifests by component for the current release.
#!/bin/bash
# An Ubuntu Package Counter - Counts all vs installed and creates manifests by component for the current release.
# Set message
export TITLE='\e[0;1;38;2;233;84;32mUbuntu Pro\e[0;1m™ Package Counter\e[0m'
export GBMSG="Thank you for using \e[0;1;38;2;233;84;32mUbuntu\x21"
# Parameters
export PROG="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/${BASH_SOURCE[0]##*/}"
export DARCH=$(dpkg --print-architecture)
export REL=$(lsb_release -cs 2>/dev/null|sed 's/^.*$/\u&/')
export VER=$(lsb_release -rs 2>/dev/null)
[[ -f /etc/machine-id ]] && { export MID=$(cat /etc/machine-id); }
[[ -z ${MID} && -f /var/lib/dbus/machine-id ]] && { export MID=$(cat /var/lib/dbus/machine-id); }
[[ -z ${MID} ]] && { export MID=unknown; }
# Begin output
printf "\n\e[1G${TITLE}\e[0m\n\n\e[2G\e[1mSystem Info:\e[0m $(ubuntu-distro-info --series=${REL,,} -f) (${DARCH})\n"
[[ -n ${MID} ]] && { printf "\e[2G\e[1mMachine ID:\e[0m\x20${MID}\n\n"; }
printf "\e[2G\e[1mPreparing environment...\e[0m\n"
printf "\r\e[K\r\e[3G - Removing any existing manifest.s..\e[K\r"
# Cleanup old manifests
find ~/ -maxdepth 1 -type f -regextype "posix-extended" -iregex '.*ubuntu-.*\.(installed|all).manifest' -delete
printf "\r\e[K\r\e[3G - Updating APT caches...\e[K\r"
sudo apt update &>/dev/null
printf "\r\e[K\r\e[3G - Creating list of all APT sections...\e[K\r"
# This array is for main since the sections won't start with a component
declare -ag SECTIONS=($(apt -qq 2>/dev/null show \*|awk '/^Section/{gsub(/.*\//,"",$2);print $2}'|sort -uV))
# Create arrays of all vs installed packages by component
printf "\e[2G\e[1mParsing APT components...\e[0m\n"
printf "\r\e[3G - Parsing \e[1mMain\e[0m\e[K\r"
declare -ag MAIN_LIST=($((printf '%s\n' ${SECTIONS[@]}|xargs -rn1 -P0 bash -c 'apt -qq 2>/dev/null list "?section(^$0$)"'|sed -r '/^$/d')|sort -uV|awk -F"/| " '{OFS="|";print $1,$3}'))
declare -ag MAIN_LIST_INSTALLED=($((printf '%s\n' ${SECTIONS[@]}|xargs -rn1 -P0 bash -c 'apt -qq 2>/dev/null list --installed "?section(^$0$)"'|sed -r '/^$/d')|sort -uV|awk -F"/| " '{OFS="|";print $1,$3}'))
printf "\r\e[3G - Parsing \e[1mUniverse\e[0m\e[K\r"
declare -ag UNIVERSE_LIST=($((apt -qq 2>/dev/null list '?section(^universe/)'|sed -r '/^$/d')|sort -uV|awk -F"/| " '{OFS="|";print $1,$3}'))
declare -ag UNIVERSE_LIST_INSTALLED=($((apt -qq 2>/dev/null list --installed '?section(^universe/)'|sed -r '/^$/d')|sort -uV|awk -F"/| " '{OFS="|";print $1,$3}'))
printf "\r\e[3G - Parsing \e[1mMultiverse\e[0m\e[K\r"
declare -ag MULTIVERSE_LIST=($((apt -qq 2>/dev/null list '?section(^multiverse/)'|sed -r '/^$/d')|sort -uV|awk -F"/| " '{OFS="|";print $1,$3}'))
declare -ag MULTIVERSE_LIST_INSTALLED=($((apt -qq 2>/dev/null list --installed '?section(^multiverse/)'|sed -r '/^$/d')|sort -uV|awk -F"/| " '{OFS="|";print $1,$3}'))
printf "\r\e[3G - Parsing \e[1mRestricted\e[0m\e[K\r"
declare -ag RESTRICTED_LIST=($((apt -qq 2>/dev/null list '?section(^restricted/)'|sed -r '/^$/d')|sort -uV|awk -F"/| " '{OFS="|";print $1,$3}'))
declare -ag RESTRICTED_LIST_INSTALLED=($((apt -qq 2>/dev/null list --installed '?section(^restricted/)'|sed -r '/^$/d')|sort -uV|awk -F"/| " '{OFS="|";print $1,$3}'))
printf '\r\e[K\r\e[1A\e[K\r\e[2G\e[1mParsing APT components \e[0;1;38;2;0;255;0m✓\e[0m\e[2B\n'
export TOTAL_INSTALLED=$((${#MAIN_LIST_INSTALLED[@]}+${#UNIVERSE_LIST_INSTALLED[@]}+${#MULTIVERSE_LIST_INSTALLED[@]}+${#RESTRITECTED_LIST_INSTALLED[@]}))
export TOTAL_ALL=$((${#MAIN_LIST[@]}+${#UNIVERSE_LIST[@]}+${#MULTIVERSE_LIST[@]}+${#RESTRITECTED_LIST[@]}))
# Display Package Counts
(((printf "Component|Installed|Available\n─────────|─────────|─────────\n";
for i in main universe multiverse restricted;do
eval printf '%s\|%s\|%s\\n' ${i} \$\{\#${i^^}_LIST_INSTALLED[@]\} \$\{\#${i^^}_LIST[@]\};
done;
printf "|═════════|═════════\n\nTotals|${TOTAL_INSTALLED}|${TOTAL_ALL}")|column -ets"|")| \
sed -r 's/^/\x20\x20/g;s/Compon.*$|Totals.*$/'$(printf "\e[1m&\e[0m")'/g')
# Create manifests
printf "\n\e[2G\e[1mCreating Package + Version manifests...\e[0m\n"
for i in main universe multiverse restricted;do
printf "\r\e[3G - Creating \e[1mubuntu-${REL}_${i,,}_${DARCH}.installed.manifest\e[0m\e[K\r"
eval printf '%s\\n' \$\{${i^^}_LIST_INSTALLED[@]\}|sed -r 's/\|/\t/g'| tee 1>/dev/null ~/ubuntu-${REL}_${i,,}_${DARCH}.installed.manifest;
printf "\r\e[3G - Creating \e[1mubuntu-${REL}_${i,,}_${DARCH}.all.manifest\e[0m\e[K\r"
eval printf '%s\\n' \$\{${i^^}_LIST[@]\}|sed -r 's/\|/\t/g'| tee 1>/dev/null ~/ubuntu-${REL}_${i,,}_${DARCH}.all.manifest;
done;
printf '\r\e[K\r\e[1A\e[K\r\e[2G\e[1mCreating Package + Version manifests \e[0;1;38;2;0;255;0m✓\e[0m\e[2B\n'
declare -ag MANIFESTS=($((find ~/ -maxdepth 1 -type f -regextype "posix-extended" -iregex '.*ubuntu-.*\.(installed|all).manifest'|sort -uV)|sed -r '/main.*|universe.*/!H;//p;$!d;g;s/\n//'))
# Show location of manifest files
printf "\n\e[2G\e[1mInstalled Package Manifests:\e[0m\n\n"
(printf '\e[3G - %s\n' ${MANIFESTS[@]}|sed -r '/\.all\.manifest/d'|sed -r '/main.*|universe.*/!H;//p;$!d;g;s/\n//')
printf "\n\e[2G\e[1mAll Packages:\e[0m\n\n"
(printf '\e[3G - %s\n' ${MANIFESTS[@]}|sed -r '/\.installed\.manifest/d'|sed -r '/main.*|universe.*/!H;//p;$!d;g;s/\n//')
# Print message that we are done
printf "\n\e[1G${TITLE} complete.\e[0m\n\n${GBMSG}\n\n"
# Clean up vars
unset MAIN_LIST* MULTIVERSE_LIST* UNIVERSE_LIST* RESTRICTED_LIST* MANIFESTS VER REL PROG EVENT DARCH TOTAL* TITLE SNAME GBMSG MID
# Script complete.
exit 0
@ThinGuy
Copy link
Author

ThinGuy commented Jun 20, 2025

Note: sudo is required to run apt update

craigbender@cb-hppeb17:~$  pro-pkg-counter.sh 

Ubuntu Pro™ Package Counter

 System Info: Ubuntu 25.04 "Plucky Puffin" (amd64)
 Machine ID: 367bec2c0d4348f499230e8202dd6d24

 Preparing environment...
 Parsing APT components ✓

  Component   Installed  Available
  ─────────   ─────────  ─────────
  main        1760       6374
  universe    175        67268
  multiverse  2          1095
  restricted  0          437
              ═════════  ═════════
  Totals      1937       74737

 Creating Package + Version manifests ✓


 Installed Package Manifests:

   - /home/craigbender/ubuntu-Plucky_main_amd64.installed.manifest
   - /home/craigbender/ubuntu-Plucky_universe_amd64.installed.manifest
   - /home/craigbender/ubuntu-Plucky_multiverse_amd64.installed.manifest
   - /home/craigbender/ubuntu-Plucky_restricted_amd64.installed.manifest

 All Packages:

   - /home/craigbender/ubuntu-Plucky_main_amd64.all.manifest
   - /home/craigbender/ubuntu-Plucky_universe_amd64.all.manifest
   - /home/craigbender/ubuntu-Plucky_multiverse_amd64.all.manifest
   - /home/craigbender/ubuntu-Plucky_restricted_amd64.all.manifest

Ubuntu Pro™ Package Counter complete.

Thank you for using Ubuntu!

craigbender@cb-hppeb17:~$ 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment