Skip to content

Instantly share code, notes, and snippets.

@Justinzobel
Last active July 22, 2025 02:57
Show Gist options
  • Save Justinzobel/e062eceecaa31dde689c5d8ec383af6c to your computer and use it in GitHub Desktop.
Save Justinzobel/e062eceecaa31dde689c5d8ec383af6c to your computer and use it in GitHub Desktop.
Alpine - list upgradeable packages in a nice coloured table
#!/bin/sh
echo 'Updating repository data ...'
apk update
# List upgradeable packages into a temporary file
apk list --upgradeable | sed -e 's/{//g' -e 's/}//g' -e 's/]//g' > /tmp/apk-upgradeable
# Add column headers
echo 'Package,Installed Version,New Version' > /tmp/apk-upgradeable-final
# Iterate over the file and remove the license stuff in the middle
while read line
do
# existing package
section1=$(echo ${line} | cut -d ' ' -f 1)
section2=$(echo ${line} | cut -d \) -f 2)
newver=$(echo ${section1} | rev | cut -d '-' -f 1-2 | rev)
name=$(echo ${section1} | rev | cut -d '-' -f 3- | rev)
oldver=$(echo ${section2} | rev | cut -d '-' -f 1-2 | rev)
echo "${name},${oldver},${newver}" >> /tmp/apk-upgradeable-final
done < /tmp/apk-upgradeable
# Set the file variable as the file passed.
file=/tmp/apk-upgradeable-final
# Colours (just comment out or set to blank for no colour)
lineColour1='\e[1;30m'
lineColour2=''
headerColour='\e[1;33m'
borderColour='\e[3;34m'
# Clean up temp file if it exists before starting
# TODO use a bash array to store this data
if [[ -f /tmp/table-column-sizes ]];then rm /tmp/table-column-sizes;fi
# Get total lines in the csv file
total=$(wc -l ${file} | cut -d ' ' -f 1)
# Find longest line for each column
i=1
while true
do
if [[ ${i} -eq ${total} ]];then break;fi
echo $(cat ${file} | cut -d , -f ${i} | wc -L | cut -d ' ' -f 1) >> /tmp/table-column-sizes
i=$(( i + 1 ))
done
# Get total number of columns
commas=$(head -n 1 ${file} | sed 's/[^,]//g' | awk '{ print length }')
columns=$(( ${commas} + 1 ))
# Print the header
function print_header {
printf "${borderColour}┌"
for x in $(seq 1 ${columns})
do
maxTmp=$(sed "${x}q;d" /tmp/table-column-sizes)
max=$(expr ${maxTmp} + 2)
for o in $(seq 1 ${max})
do printf '─'
done
if [[ ${x} -eq ${columns} ]];then printf '┐\n';else printf '┬';fi
done
printf '│\e[0m'
for header in $(seq 1 ${columns})
do
headertext=$(head -n 1 ${file} | cut -d , -f ${header})
printf " ${headerColour}%s\e[0m" "${headertext}"
# Print padding
textlen=$(echo ${headertext} | wc -c)
maxTmp=$(sed "${header}q;d" /tmp/table-column-sizes)
max=$(expr ${maxTmp} + 3)
diff=$(expr ${max} - ${textlen} - 1)
for k in $(seq 1 ${diff})
do
printf ' '
done
printf "${borderColour}│\e[0m"
done
printf '\n'
printf "${borderColour}├"
for a in $(seq 1 ${columns})
do
maxTmp=$(sed "${a}q;d" /tmp/table-column-sizes)
max=$(expr ${maxTmp} + 2)
for b in $(seq 1 ${max})
do printf '─'
done
if [[ ${a} -eq ${columns} ]];then printf '┤\e[0m\n';else printf '┼';fi
done
}
# Print each row
function print_data {
line=${1}
printf "${borderColour}│\e[0m"
for data in $(seq 1 ${columns})
do
datatext=$(sed "${line}q;d" ${file} | cut -d , -f ${data})
if [[ ${fancy} == 'true' ]]
then
printf " ${lineColour1}%s\e[0m" "${datatext}"
else
printf " ${lineColour2}%s\e[0m" "${datatext}"
fi
# Print padding
textlen=$(echo ${datatext} | wc -c)
maxTmp=$(sed "${data}q;d" /tmp/table-column-sizes)
max=$(expr ${maxTmp} + 3)
diff=$(expr ${max} - ${textlen} - 1)
for k in $(seq 1 ${diff})
do
printf ' '
done
printf "${borderColour}│\e[0m"
done
printf '\n'
}
# Iterate over each row in the CSV file
i=1
while true
do
if [[ $((${i} % 2)) -eq 0 ]];then fancy=false;else fancy=true;fi
if [[ ${i} -eq 1 ]];then print_header;else print_data ${i};fi
if [[ ${i} -eq ${total} ]];then break;fi
i=$(( i + 1 ))
done
# Print the bottom of the table
printf "${borderColour}└"
for g in $(seq 1 ${columns})
do
maxTmp=$(sed "${g}q;d" /tmp/table-column-sizes)
max=$(expr ${maxTmp} + 2)
for h in $(seq 1 ${max})
do printf '─'
done
if [[ ${g} -eq ${columns} ]];then printf '┘\n\e[0m';else printf '┴';fi
done
# Clean up temp file
if [[ -f /tmp/table-column-sizes ]];then rm /tmp/table-column-sizes;fi
# Print hostname so user knows if they're on a remote device
echo "Hostname: $(hostname)"
# Ask if user wants to proceed with the upgrade
read -p 'Upgrade (yes/no): ' ans
if [[ ${ans} == 'yes' ]]
then
echo "Beginning upgrade ..."
apk upgrade -a
else
echo "Aborting"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment