Updates a debian-linux-based computer that uses APT.
#!/bin/bash
ask_for_sudo() {
    # Ask for the administrator password upfront.
    sudo -v &> /dev/null
    # Update existing `sudo` time stamp
    # until this script has finished.
    #
    # https://gist.github.com/cowboy/3118588
    while true; do
        sudo -n true
        sleep 60
        kill -0 "$$" || exit
    done &> /dev/null &
}
show_spinner() {
	local -r FRAMES='/-\|'
    # shellcheck disable=SC2034
    local -r NUMBER_OR_FRAMES=${#FRAMES}
    local -r CMDS="$2"
    local -r MSG="$3"
    local -r PID="$1"
    local i=0
    local frameText=""
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # Note: In order for the Travis CI site to display
    # things correctly, it needs special treatment, hence,
    # the "is Travis CI?" checks.
    if [ "$TRAVIS" != "true" ]; then
        # Provide more space so that the text hopefully
        # doesn't reach the bottom line of the terminal window.
        #
        # This is a workaround for escape sequences not tracking
        # the buffer position (accounting for scrolling).
        #
        # See also: https://unix.stackexchange.com/a/278888
        printf "\n\n\n"
        tput cuu 3
        tput sc
    fi
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # Display spinner while the commands are being executed.
    while kill -0 "$PID" &>/dev/null; do
        frameText="   [${FRAMES:i++%NUMBER_OR_FRAMES:1}] $MSG"
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        # Print frame text.
        if [ "$TRAVIS" != "true" ]; then
            printf "%s\n" "$frameText"
        else
            printf "%s" "$frameText"
        fi
        sleep 0.2
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        # Clear frame text.
        if [ "$TRAVIS" != "true" ]; then
            tput rc
        else
            printf "\r"
        fi
    done
}
execute() {
    local -r CMDS="$1"
    local -r MSG="$2"
    local -r TMP_FILE="$(mktemp /tmp/XXXXX)"
    local exitCode=0
    local cmdsPID=""
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # If the current process is ended,
    # also end all its subprocesses.
    set_trap "EXIT" "kill_all_subprocesses"
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # Execute commands in background
    eval "$CMDS" \
        &> /dev/null \
        2> "$TMP_FILE" &
    cmdsPID=$!
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # Show a spinner if the commands
    # require more time to complete.
    show_spinner "$cmdsPID" "$CMDS" "$MSG"
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # Wait for the commands to no longer be executing
    # in the background, and then get their exit code.
    wait "$cmdsPID" &> /dev/null
    exitCode=$?
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # Print output based on what happened.
    print_result $exitCode "$MSG"
    if [ $exitCode -ne 0 ]; then
        print_error_stream < "$TMP_FILE"
    fi
    rm -rf "$TMP_FILE"
    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    return $exitCode
}
set_trap() {
    trap -p "$1" | grep "$2" &> /dev/null \
        || trap '$2' "$1"
}
print_error_stream() {
    while read -r line; do
        print_error "↳ ERROR: $line"
    done
}
print_error() {
    print_in_red "   [✖] $1 $2\n"
}
print_success() {
    print_in_green "   [✔] $1\n"
}
print_in_green() {
    print_in_color "$1" 2
}
print_in_purple() {
    print_in_color "$1" 5
}
print_in_red() {
    print_in_color "$1" 1
}
print_in_color() {
    printf "%b" \
        "$(tput setaf "$2" 2> /dev/null)" \
        "$1" \
        "$(tput sgr0 2> /dev/null)"
}
print_result() {
    if [ "$1" -eq 0 ]; then
        print_success "$2"
    else
        print_error "$2"
    fi
    return "$1"
}
fix_dpkg() {
    declare -a files=("/var/lib/dpkg/lock" "/var/cache/apt/archives/lock")
    for i in "${files[@]}"
    do
        # If there is a dpkg lock, then remove it.
        if [ -e "$i" ]; then
            sudo rm -rf "$i" &> /dev/null
        fi
    done
}
install_package() {
    declare -r PACKAGE="$2"
    declare -r PACKAGE_READABLE_NAME="$1"
    if ! package_is_installed "$PACKAGE"; then
        fix_dpkg
        execute "sudo apt-get install --allow-unauthenticated -qqy $PACKAGE" "$PACKAGE_READABLE_NAME"
        #                                      suppress output ─┘│
        #            assume "yes" as the answer to all prompts ──┘
    else
        print_success "$PACKAGE_READABLE_NAME"
    fi
}
package_is_installed() {
    dpkg -s "$1" &> /dev/null
}
update() {
	ask_for_sudo
	fix_dpkg
	# Resynchronize the package index files from their sources.
	execute \
		"sudo apt-get update -qqy" \
		"APT (update)"
}
upgrade() {
	ask_for_sudo
	
	fix_dpkg
	# Install the newest versions of all packages installed.
	execute \
		"export DEBIAN_FRONTEND=\"noninteractive\" \
			&& sudo apt-get -o Dpkg::Options::=\"--force-confnew\" upgrade -qqy" \
		"APT (upgrade)"
}
main() {
	print_in_purple "\n   Updating System\n\n"
	update
	upgrade
	
	printf "\n"
}
mainHere's a short shell script to quickly update a debian-linux-based computer that uses APT.
bash <(curl -s https://gist.githubusercontent.com/nicholasadamou/5fbde132620829a9c68c94cb04c013c8/raw/b1ab3ffe6f1b1dd425ff89920d88a5d169e2506e/update.sh)