Skip to content

Instantly share code, notes, and snippets.

@bluPhy
Last active November 4, 2025 14:28
Show Gist options
  • Select an option

  • Save bluPhy/e9979ef668d959c40ec70700dfb9fa92 to your computer and use it in GitHub Desktop.

Select an option

Save bluPhy/e9979ef668d959c40ec70700dfb9fa92 to your computer and use it in GitHub Desktop.
Script to keep Linux updated and basic clean-up
#!/usr/bin/env bash
set -euo pipefail # Exit on error, undefined variables, and pipe failures
IFS=$'\n\t' # Set secure Internal Field Separator
# Configuration
readonly SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_VERSION="2.0.0"
readonly LOG_FILE="${HOME}/.cache/system-update.log"
# Color codes
readonly COLOR_RESET="\033[0m"
readonly COLOR_INFO="\033[0;96m"
readonly COLOR_SUCCESS="\033[0;92m"
readonly COLOR_WARNING="\033[0;93m"
readonly COLOR_DANGER="\033[0;91m"
# Global variables
VERBOSE=false
DRY_RUN=false
SKIP_CONFIRMATION=false
# Function to show usage
usage() {
cat <<EOF
Usage: $SCRIPT_NAME [OPTIONS]
A comprehensive system update script for various package managers and tools.
OPTIONS:
-h, --help Show this help message
-v, --verbose Enable verbose output
-d, --dry-run Show what would be done without executing
-y, --yes Skip confirmation prompts
-l, --log Enable logging to $LOG_FILE
--version Show script version
Examples:
$SCRIPT_NAME # Run with default settings
$SCRIPT_NAME -v # Run with verbose output
$SCRIPT_NAME -d # Dry run mode
$SCRIPT_NAME -y # Skip confirmations
EOF
}
# Enhanced message function with logging support
msg() {
local message="$1"
local type="${2:-}"
local color=""
case "$type" in
info) color="$COLOR_INFO" ;;
success) color="$COLOR_SUCCESS" ;;
warning) color="$COLOR_WARNING" ;;
danger) color="$COLOR_DANGER" ;;
*) color="$COLOR_RESET" ;;
esac
# Print to console
printf "${color}%b${COLOR_RESET}" "$message" >&2
# Log if enabled
if [[ "${LOGGING:-false}" == "true" ]]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') [$type] $message" | sed 's/\\n//g' >> "$LOG_FILE"
fi
}
# Enhanced command existence check
commandExists() {
local cmd="$1"
if command -v "$cmd" &>/dev/null; then
[[ "$VERBOSE" == "true" ]] && msg "✓ Command $cmd exists\n" "success"
return 0
else
[[ "$VERBOSE" == "true" ]] && msg "✗ Command $cmd not found\n" "warning"
return 1
fi
}
# Check if running with appropriate privileges
checkPrivileges() {
if [[ $EUID -eq 0 ]]; then
msg "Warning: Running as root is not recommended\n" "warning"
msg "Consider running as a regular user with sudo privileges\n" "warning"
read -p "Continue anyway? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
# Check sudo access
if ! sudo -n true 2>/dev/null; then
msg "This script requires sudo privileges. Please enter your password.\n" "info"
sudo -v
fi
}
# Function to detect operating system and CPU architecture
detectOsAndArch() {
local os=$(uname -s)
local arch=$(uname -m)
local os_pretty_name=""
case "$os" in
Linux)
if [[ -f /etc/os-release ]]; then
source /etc/os-release
os_pretty_name="${PRETTY_NAME:-$ID ${VERSION_ID:-}}"
elif [[ -f /etc/redhat-release ]]; then
os_pretty_name=$(< /etc/redhat-release)
elif [[ -f /etc/SuSE-release ]]; then
os_pretty_name=$(< /etc/SuSE-release)
else
os_pretty_name="Unknown Linux Distribution"
fi
;;
Darwin)
os_pretty_name="$(sw_vers -productName) $(sw_vers -productVersion)"
;;
*BSD)
os_pretty_name="$os $(uname -r)"
;;
*)
os_pretty_name="Unknown OS: $os"
;;
esac
msg "\n=== System Information ===\n" "info"
msg "OS: $os_pretty_name\n" "info"
msg "Architecture: $arch\n" "info"
msg "Hostname: $(hostname -f 2>/dev/null || hostname)\n" "info"
msg "User: $USER\n" "info"
# Export for use in other functions
export DETECTED_OS="$os"
export DETECTED_ARCH="$arch"
}
# Enhanced package manager detection with priority
detectPackageManager() {
local package_managers=(
"apt:apt-get"
"dnf:dnf"
"yum:yum"
"zypper:zypper"
"pacman:pacman"
"pkg:pkg"
"apk:apk" # Added Alpine Linux support
"emerge:emerge" # Added Gentoo support
)
for pm in "${package_managers[@]}"; do
local name="${pm%%:*}"
local cmd="${pm##*:}"
if commandExists "$cmd"; then
msg "Detected $name package manager\n" "info"
export PACKAGE_MANAGER="$name"
return 0
fi
done
msg "Warning: No supported package manager detected\n" "warning"
export PACKAGE_MANAGER=""
return 1
}
# Enhanced package update with error handling
updatePackages() {
local pm="${1:-$PACKAGE_MANAGER}"
[[ -z "$pm" ]] && return 1
msg "\n=== Updating System Packages ($pm) ===\n" "info"
if [[ "$DRY_RUN" == "true" ]]; then
msg "DRY RUN: Would update packages using $pm\n" "warning"
return 0
fi
case "$pm" in
apt)
sudo DEBIAN_FRONTEND=noninteractive apt-get update || return 1
sudo DEBIAN_FRONTEND=noninteractive apt-get -o APT::Get::Always-Include-Phased-Updates=true dist-upgrade -y
sudo DEBIAN_FRONTEND=noninteractive apt-get autoremove -y
sudo DEBIAN_FRONTEND=noninteractive apt-get autoclean
;;
dnf|yum)
sudo $pm update -y || return 1
sudo $pm autoremove -y
sudo $pm clean all
;;
zypper)
sudo zypper --non-interactive refresh || return 1
sudo zypper --non-interactive dup -y
sudo zypper --non-interactive clean --all
;;
pacman)
sudo pacman -Syy || return 1
sudo pacman -Syu --noconfirm
# Clean package cache (keep last 3 versions)
sudo paccache -r -k 3 2>/dev/null || true
;;
pkg)
sudo pkg update || return 1
sudo pkg upgrade -y
sudo pkg autoremove -y
;;
apk)
sudo apk update || return 1
sudo apk upgrade
;;
emerge)
sudo emerge --sync || return 1
sudo emerge -uDN @world
;;
*)
msg "Unsupported package manager: $pm\n" "danger"
return 1
;;
esac
msg "System packages updated successfully\n" "success"
}
# Enhanced Flatpak update with better error handling
updateFlatpak() {
if ! commandExists flatpak; then
return 0
fi
msg "\n=== Updating Flatpak Packages ===\n" "info"
if [[ "$DRY_RUN" == "true" ]]; then
msg "DRY RUN: Would update Flatpak packages\n" "warning"
flatpak remote-ls --updates 2>/dev/null || true
return 0
fi
# Update both system and user Flatpaks
flatpak update -y --noninteractive 2>/dev/null || true
flatpak update -y --noninteractive --user 2>/dev/null || true
# Remove unused runtimes
flatpak uninstall --unused -y --noninteractive 2>/dev/null || true
msg "Flatpak packages updated\n" "success"
}
# Enhanced Snap update
updateSnap() {
if ! commandExists snap; then
return 0
fi
msg "\n=== Updating Snap Packages ===\n" "info"
if [[ "$DRY_RUN" == "true" ]]; then
msg "DRY RUN: Would update Snap packages\n" "warning"
snap list --all | tail -n +2 || true
return 0
fi
sudo snap refresh 2>/dev/null || true
# Remove old snap revisions to save space
if [[ "$VERBOSE" == "true" ]]; then
msg "Removing old snap revisions...\n" "info"
snap list --all | awk '/disabled/{print $1, $3}' |
while read snapname revision; do
sudo snap remove "$snapname" --revision="$revision" 2>/dev/null || true
done
fi
msg "Snap packages updated\n" "success"
}
# Enhanced Homebrew update
updateHomebrew() {
if ! commandExists brew; then
return 0
fi
msg "\n=== Updating Homebrew ===\n" "info"
if [[ "$DRY_RUN" == "true" ]]; then
msg "DRY RUN: Would update Homebrew packages\n" "warning"
brew outdated || true
return 0
fi
# Update Homebrew itself
brew update
# Upgrade packages
brew upgrade
# Upgrade casks
brew upgrade --cask
# Cleanup
brew cleanup -s
brew autoremove
# Run diagnostics if verbose
if [[ "$VERBOSE" == "true" ]]; then
brew doctor || true
fi
msg "Homebrew updated successfully\n" "success"
}
# Enhanced container image update with better error handling
updateContainerImages() {
local container_cmd=""
if commandExists docker; then
container_cmd="docker"
elif commandExists podman; then
container_cmd="podman"
else
return 0
fi
msg "\n=== Updating Container Images ($container_cmd) ===\n" "info"
if [[ "$DRY_RUN" == "true" ]]; then
msg "DRY RUN: Would update $container_cmd images\n" "warning"
sudo $container_cmd images --format 'table {{.Repository}}\t{{.Tag}}\t{{.Size}}' || true
return 0
fi
# Get list of images (excluding none tags)
local images
images=$(sudo $container_cmd images --format '{{.Repository}}:{{.Tag}}' | grep -v '<none>' | sort -u)
if [[ -z "$images" ]]; then
msg "No container images found\n" "info"
return 0
fi
# Update each image
while IFS= read -r image; do
msg "Updating $image...\n" "info"
sudo $container_cmd pull "$image" 2>/dev/null || msg "Failed to update $image\n" "warning"
done <<< "$images"
# Cleanup
msg "Cleaning up container system...\n" "info"
# Remove dangling images
local dangling
dangling=$(sudo $container_cmd images -f "dangling=true" -q)
if [[ -n "$dangling" ]]; then
echo "$dangling" | xargs sudo $container_cmd rmi 2>/dev/null || true
msg "Removed dangling images\n" "success"
fi
# Prune system (with confirmation in non-skip mode)
if [[ "$SKIP_CONFIRMATION" == "true" ]] || [[ "$container_cmd" == "podman" ]]; then
sudo $container_cmd system prune -af 2>/dev/null || true
else
sudo $container_cmd system prune -a 2>/dev/null || true
fi
msg "Container images updated\n" "success"
}
# Update other tools
updateMiscTools() {
msg "\n=== Updating Miscellaneous Tools ===\n" "info"
# GitHub CLI extensions
if commandExists gh; then
msg "Updating GitHub CLI extensions...\n" "info"
if [[ "$DRY_RUN" == "false" ]]; then
gh extension upgrade --all 2>/dev/null || true
fi
fi
# Update pip packages
if commandExists pip3; then
msg "Updating pip packages...\n" "info"
if [[ "$DRY_RUN" == "false" ]]; then
pip3 list --outdated --format=json | \
python3 -c "import json, sys; print('\n'.join([x['name'] for x in json.load(sys.stdin)]))" | \
xargs -n1 pip3 install -U 2>/dev/null || true
fi
fi
# Update npm global packages
if commandExists npm; then
msg "Updating npm global packages...\n" "info"
if [[ "$DRY_RUN" == "false" ]]; then
npm update -g 2>/dev/null || true
fi
fi
msg "Miscellaneous tools updated\n" "success"
}
# System cleanup
systemCleanup() {
msg "\n=== System Cleanup ===\n" "info"
if [[ "$DRY_RUN" == "true" ]]; then
msg "DRY RUN: Would perform system cleanup\n" "warning"
return 0
fi
# Clean journal logs
if commandExists journalctl; then
msg "Cleaning journal logs...\n" "info"
sudo journalctl --vacuum-time=7d --vacuum-size=1G 2>/dev/null || true
fi
# Clean temp files (be careful!)
if [[ -d /tmp ]]; then
msg "Cleaning old temp files...\n" "info"
find /tmp -type f -atime +7 -delete 2>/dev/null || true
fi
# Clean user cache
if [[ -d "$HOME/.cache" ]]; then
msg "Cleaning old cache files...\n" "info"
find "$HOME/.cache" -type f -atime +30 -delete 2>/dev/null || true
fi
msg "System cleanup completed\n" "success"
}
# Summary report
showSummary() {
msg "\n=== Update Summary ===\n" "success"
msg "✓ All updates completed successfully\n" "success"
# Show disk usage
msg "\nDisk usage:\n" "info"
df -h / | tail -n 1
# Show if reboot is required (for systems that support it)
if [[ -f /var/run/reboot-required ]]; then
msg "\n⚠ System reboot required\n" "warning"
fi
}
# Parse command line arguments
parseArguments() {
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
usage
exit 0
;;
-v|--verbose)
VERBOSE=true
shift
;;
-d|--dry-run)
DRY_RUN=true
msg "DRY RUN MODE ENABLED\n" "warning"
shift
;;
-y|--yes)
SKIP_CONFIRMATION=true
shift
;;
-l|--log)
LOGGING=true
mkdir -p "$(dirname "$LOG_FILE")"
msg "Logging enabled: $LOG_FILE\n" "info"
shift
;;
--version)
echo "$SCRIPT_NAME version $SCRIPT_VERSION"
exit 0
;;
*)
msg "Unknown option: $1\n" "danger"
usage
exit 1
;;
esac
done
}
# Main execution
main() {
# Parse arguments
parseArguments "$@"
# Header
msg "========================================\n" "info"
msg " System Update Script v$SCRIPT_VERSION\n" "info"
msg "========================================\n" "info"
# Check privileges
checkPrivileges
# Detect system
detectOsAndArch
# Confirmation
if [[ "$SKIP_CONFIRMATION" == "false" ]] && [[ "$DRY_RUN" == "false" ]]; then
msg "\nThis script will update all package managers and tools.\n" "warning"
read -p "Continue? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
msg "Update cancelled\n" "info"
exit 0
fi
fi
# Run updates
detectPackageManager && updatePackages
updateFlatpak
updateSnap
updateHomebrew
updateContainerImages
updateMiscTools
systemCleanup
# Summary
showSummary
msg "\n✓ All operations completed\n" "success"
}
# Error handling
trap 'msg "\nError occurred on line $LINENO\n" "danger"; exit 1' ERR
# Run main function
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment