Combine update.sh + clean.sh into housekeeping.sh with subcommand dispatch
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 38s

- Merges both scripts into a single unified housekeeping.sh
- Subcommands: update, clean, omz, all (default)
- New 'omz' command: runs 'omz update' with safe fallback if not installed
- Unknown subcommands print usage and exit 0
- Shared utility functions defined once at top
- Preserves all existing OS detection, cleanup, and reporting logic
This commit is contained in:
2026-07-05 13:08:53 -03:00
parent 4cdb79dcd5
commit d78c4946bd
2 changed files with 366 additions and 326 deletions

View File

@@ -1,6 +1,8 @@
#!/bin/bash
# System Cleanup and Maintenance Script
# Housekeeping Script
# Unified system maintenance: package index update, system cleanup, and oh-my-zsh update.
# Combines the former update.sh and clean.sh into a single script with subcommand dispatch.
set -euo pipefail
@@ -24,6 +26,8 @@ readonly CACHE_RETENTION_DAYS=7
readonly TEMP_DIRS=("/tmp" "/var/tmp")
readonly CACHE_DIRS=("/var/cache/apt" "/root/.cache")
readonly SCRIPT_NAME="$(basename "$0")"
#==============================================================================
# UTILITY FUNCTIONS
#==============================================================================
@@ -61,31 +65,31 @@ get_dir_size() {
clean_directory() {
local dir="$1"
local description="$2"
if [[ ! -d "$dir" ]]; then
return 0
fi
local size_before
size_before=$(get_dir_size "$dir")
if [[ "$size_before" == "0B" ]]; then
log_info "$description: already clean"
return 0
fi
log_step "$description (was $size_before)..."
# Use find to delete files older than retention period
# -type f -mtime +N: only files older than N days (not directories)
local deleted=false
if find "$dir" -type f -mtime +"${CACHE_RETENTION_DAYS:-7}" -delete 2>/dev/null; then
deleted=true
fi
# Clean up empty directories left behind (but don't remove the dir itself)
find "$dir" -mindepth 1 -type d -empty -delete 2>/dev/null || true
if $deleted; then
local size_after
size_after=$(get_dir_size "$dir")
@@ -102,7 +106,7 @@ clean_directory() {
# Get system information for reporting
get_system_info() {
local info=""
# Memory info
if [[ -f /proc/meminfo ]]; then
local mem_total mem_available
@@ -112,7 +116,7 @@ get_system_info() {
info+="Memory: $((mem_available/1024))MB available of $((mem_total/1024))MB total"
fi
fi
# Disk space info
if command_exists df; then
local disk_info
@@ -121,10 +125,163 @@ get_system_info() {
info+="${info:+, }Disk: $disk_info"
fi
fi
echo "$info"
}
#==============================================================================
# OS DETECTION (for package index update)
#==============================================================================
# Detect the running OS and its ID string.
# Prints two values: the distro id (lowercase) and the family id (lowercase).
detect_os() {
local id=""
local family=""
if [[ -f /etc/os-release ]]; then
# shellcheck disable=SC1091
. /etc/os-release
id="${ID:-unknown}"
family="${ID_LIKE:-}"
elif [[ -f /etc/alpine-release ]]; then
id="alpine"
family="alpine"
else
# Fall back to package manager detection
if command_exists apk; then
id="alpine"; family="alpine"
elif command_exists pacman; then
id="arch"; family="arch"
elif command_exists apt-get; then
id="debian"; family="debian"
elif command_exists dnf; then
id="fedora"; family="rhel"
elif command_exists yum; then
id="centos"; family="rhel"
elif command_exists zypper; then
id="opensuse"; family="suse"
elif command_exists xbps-install; then
id="void"; family="void"
elif command_exists emerge; then
id="gentoo"; family="gentoo"
else
id="unknown"; family="unknown"
fi
fi
echo "${id} ${family}"
}
#==============================================================================
# OS-SPECIFIC PACKAGE INDEX UPDATE FUNCTIONS
#==============================================================================
update_alpine() {
if ! command_exists apk; then
log_warning "apk not found on Alpine-type system"
return 0
fi
log_info "Running apk update..."
if apk update 2>&1; then
log_success "Alpine package index updated"
else
log_warning "apk update failed"
fi
}
update_arch() {
if ! command_exists pacman; then
log_warning "pacman not found on Arch-type system"
return 0
fi
log_info "Refreshing pacman database (pacman -Sy)..."
# -S = sync, -y = refresh local database only (no package installation)
if pacman -Sy --noconfirm 2>&1; then
log_success "Arch package database refreshed"
else
log_warning "pacman -Sy failed"
fi
}
update_debian() {
if ! command_exists apt-get; then
log_warning "apt-get not found on Debian-type system"
return 0
fi
log_info "Running apt-get update..."
if apt-get update 2>&1; then
log_success "Debian/Ubuntu package index updated"
else
log_warning "apt-get update failed"
fi
}
update_rhel() {
if command_exists dnf; then
log_info "Running dnf makecache..."
if dnf makecache 2>&1; then
log_success "RHEL/Fedora metadata cache refreshed (dnf)"
else
log_warning "dnf makecache failed"
fi
elif command_exists yum; then
log_info "Running yum makecache..."
if yum makecache fast 2>&1; then
log_success "CentOS/RHEL metadata cache refreshed (yum)"
else
log_warning "yum makecache failed"
fi
else
log_warning "No DNF or YUM found on RHEL-type system"
fi
}
update_suse() {
if ! command_exists zypper; then
log_warning "zypper not found on openSUSE-type system"
return 0
fi
log_info "Running zypper refresh..."
if zypper --non-interactive refresh 2>&1; then
log_success "openSUSE repositories refreshed"
else
log_warning "zypper refresh failed"
fi
}
update_void() {
if ! command_exists xbps-install; then
log_warning "xbps-install not found on Void system"
return 0
fi
log_info "Running xbps-install -S (sync repository index)..."
if xbps-install -S 2>&1; then
log_success "Void package index synced"
else
log_warning "xbps-install -S failed"
fi
}
update_gentoo() {
if ! command_exists emerge; then
log_warning "emerge not found on Gentoo system"
return 0
fi
log_info "Running emaint sync -a..."
if emaint sync -a 2>&1; then
log_success "Gentoo Portage tree synced"
else
log_warning "emaint sync failed"
fi
}
#==============================================================================
# DOCKER CLEANUP FUNCTIONS
#==============================================================================
@@ -135,15 +292,15 @@ cleanup_docker() {
log_info "Docker not found, skipping Docker cleanup"
return 0
fi
log_step "Starting Docker cleanup..."
# Check if Docker daemon is running
if ! docker info >/dev/null 2>&1; then
log_warning "Docker daemon not running, skipping Docker cleanup"
return 0
fi
# Remove unused images
log_info "Removing unused Docker images..."
if docker image prune -af >/dev/null 2>&1; then
@@ -151,7 +308,7 @@ cleanup_docker() {
else
log_warning "Docker image cleanup failed"
fi
# Remove stopped containers
log_info "Removing stopped Docker containers..."
if docker container prune -f >/dev/null 2>&1; then
@@ -159,7 +316,7 @@ cleanup_docker() {
else
log_warning "Docker container cleanup failed"
fi
# Remove unused volumes
log_info "Removing unused Docker volumes..."
if docker volume prune -f >/dev/null 2>&1; then
@@ -167,7 +324,7 @@ cleanup_docker() {
else
log_warning "Docker volume cleanup failed"
fi
# Remove unused networks
log_info "Removing unused Docker networks..."
if docker network prune -f >/dev/null 2>&1; then
@@ -175,7 +332,7 @@ cleanup_docker() {
else
log_warning "Docker network cleanup failed"
fi
# Complete system cleanup
log_info "Running Docker system cleanup..."
if docker system prune -af >/dev/null 2>&1; then
@@ -194,19 +351,19 @@ cleanup_apk() {
if ! command_exists apk; then
return 0
fi
log_step "Cleaning APK cache..."
# Clean APK cache
if [[ -d /var/cache/apk ]]; then
clean_directory "/var/cache/apk" "APK cache directory"
fi
# Clean APK cache using apk command
if apk cache clean >/dev/null 2>&1; then
log_success "APK cache cleaned"
fi
# Update package index
log_info "Updating APK package index..."
if apk update >/dev/null 2>&1; then
@@ -221,30 +378,30 @@ cleanup_apt() {
if ! command_exists apt-get; then
return 0
fi
log_step "Cleaning APT cache..."
# Clean downloaded packages
if apt-get clean >/dev/null 2>&1; then
log_success "APT cache cleaned"
else
log_warning "APT clean failed"
fi
# Remove orphaned packages
if apt-get autoclean >/dev/null 2>&1; then
log_success "APT autocleaned"
else
log_warning "APT autoclean failed"
fi
# Remove unnecessary packages
if apt-get autoremove -y >/dev/null 2>&1; then
log_success "Unnecessary packages removed"
else
log_warning "APT autoremove failed"
fi
# Update package index
log_info "Updating APT package index..."
if apt-get update >/dev/null 2>&1; then
@@ -261,7 +418,7 @@ cleanup_apt() {
# Clean system temporary directories
cleanup_temp_dirs() {
log_step "Cleaning temporary directories..."
for temp_dir in "${TEMP_DIRS[@]}"; do
if [[ -d "$temp_dir" ]]; then
# Clean contents but preserve the directory
@@ -274,13 +431,13 @@ cleanup_temp_dirs() {
# Clean system cache directories
cleanup_cache_dirs() {
log_step "Cleaning cache directories..."
for cache_dir in "${CACHE_DIRS[@]}"; do
if [[ -d "$cache_dir" ]]; then
clean_directory "$cache_dir" "Cache directory $cache_dir"
fi
done
# Clean safe cache locations only
# NOTE: We do NOT recursively delete /var/cache or /root/.cache as a whole,
# as that can break package managers and remove important data.
@@ -297,7 +454,7 @@ cleanup_cache_dirs() {
"/home/*/.npm"
"/home/*/.pip"
)
for cache_pattern in "${additional_caches[@]}"; do
# Use shell expansion for patterns
for cache_path in $cache_pattern; do
@@ -311,32 +468,32 @@ cleanup_cache_dirs() {
# Clean old log files
cleanup_logs() {
log_step "Cleaning old log files..."
# Clean logs older than retention period
if [[ -d /var/log ]]; then
local cleaned_count=0
# Find and remove old log files
while IFS= read -r -d '' logfile; do
if rm -f "$logfile" 2>/dev/null; then
((++cleaned_count)) || true
fi
done < <(find /var/log -type f -name "*.log" -mtime +"$LOG_RETENTION_DAYS" -print0 2>/dev/null || true)
# Clean compressed logs
while IFS= read -r -d '' logfile; do
if rm -f "$logfile" 2>/dev/null; then
((++cleaned_count)) || true
fi
done < <(find /var/log -type f \( -name "*.log.gz" -o -name "*.log.bz2" -o -name "*.log.xz" \) -mtime +"$LOG_RETENTION_DAYS" -print0 2>/dev/null || true)
if [[ $cleaned_count -gt 0 ]]; then
log_success "Removed $cleaned_count old log files"
else
log_info "No old log files to remove"
fi
fi
# Truncate large active log files
local large_logs
while IFS= read -r -d '' logfile; do
@@ -351,16 +508,16 @@ cleanup_journal() {
if ! command_exists journalctl; then
return 0
fi
log_step "Cleaning systemd journal..."
# Clean journal older than retention period
if journalctl --vacuum-time="${JOURNAL_RETENTION_DAYS}d" >/dev/null 2>&1; then
log_success "Journal cleaned (older than $JOURNAL_RETENTION_DAYS days)"
else
log_warning "Journal cleanup failed"
fi
# Limit journal size
if journalctl --vacuum-size=100M >/dev/null 2>&1; then
log_success "Journal size limited to 100MB"
@@ -370,14 +527,14 @@ cleanup_journal() {
# Clean thumbnail caches
cleanup_thumbnails() {
log_step "Cleaning thumbnail caches..."
local thumbnail_dirs=(
"/root/.thumbnails"
"/root/.cache/thumbnails"
"/home/*/.thumbnails"
"/home/*/.cache/thumbnails"
)
for thumb_pattern in "${thumbnail_dirs[@]}"; do
for thumb_dir in $thumb_pattern; do
if [[ -d "$thumb_dir" ]]; then
@@ -390,12 +547,12 @@ cleanup_thumbnails() {
# Optimize memory caches
optimize_memory() {
log_step "Optimizing memory caches..."
# Sync filesystem
if sync; then
log_info "Filesystem synced"
fi
# Drop caches (page cache, dentries and inodes)
if [[ -w /proc/sys/vm/drop_caches ]]; then
echo 3 > /proc/sys/vm/drop_caches 2>/dev/null && log_success "Memory caches dropped" || log_warning "Failed to drop memory caches"
@@ -409,14 +566,14 @@ optimize_memory() {
# Generate cleanup summary
generate_summary() {
log_step "Generating cleanup summary..."
local system_info
system_info=$(get_system_info)
if [[ -n "$system_info" ]]; then
log_info "System status: $system_info"
fi
# Show disk usage of important directories
local important_dirs=("/" "/var" "/tmp" "/var/log" "/var/cache")
for dir in "${important_dirs[@]}"; do
@@ -429,13 +586,90 @@ generate_summary() {
}
#==============================================================================
# MAIN EXECUTION
# SUBCOMMAND HANDLERS
#==============================================================================
main() {
# update: refresh the OS package index (no upgrades)
run_update() {
log_step "Starting package list update (${SCRIPT_NAME})"
echo
# Detect the OS
local distro_info distro_id family_id
distro_info="$(detect_os)"
distro_id="${distro_info%% *}"
family_id="${distro_info##* }"
log_info "Detected OS: ${distro_id}"
if [[ -n "${family_id}" && "${family_id}" != "${distro_id}" ]]; then
log_info "OS family: ${family_id}"
fi
echo
# Dispatch to the correct handler based on distro family
# Check distro_id first, then fall back to family_id for aliases
case "${distro_id}" in
alpine)
update_alpine
;;
arch|artix|manjaro|garuda)
update_arch
;;
debian|ubuntu|linuxmint|kali|raspbian|pop)
update_debian
;;
fedora|rhel|centos|rocky|almalinux|amazon|oracle|clear-linux-os)
update_rhel
;;
opensuse*|suse|sles)
update_suse
;;
void)
update_void
;;
gentoo|funtoo)
update_gentoo
;;
*)
# Try matching on family ID as fallback
case "${family_id}" in
*alpine*)
update_alpine
;;
*arch*)
update_arch
;;
*debian*|*ubuntu*)
update_debian
;;
*rhel*|*fedora*|*centos*)
update_rhel
;;
*suse*|*opensuse*)
update_suse
;;
*void*)
update_void
;;
*gentoo*)
update_gentoo
;;
*)
die "Unsupported OS: ${distro_id} (${family_id}). Cannot update package index."
;;
esac
;;
esac
echo
log_success "Package list update completed!"
}
# clean: full system cleanup and maintenance
run_clean() {
log_step "Starting System Cleanup and Maintenance"
echo
# Show initial system status
local initial_info
initial_info=$(get_system_info)
@@ -443,31 +677,106 @@ main() {
log_info "Initial system status: $initial_info"
echo
fi
# Docker cleanup
cleanup_docker
# Package manager cleanup
cleanup_apk
cleanup_apt
# System cleanup
cleanup_temp_dirs
cleanup_cache_dirs
cleanup_logs
cleanup_journal
cleanup_thumbnails
# Memory optimization
optimize_memory
# Generate summary
echo
generate_summary
echo
log_success "System cleanup and maintenance completed!"
}
# Execute main function with all arguments
main "$@"
# omz: update oh-my-zsh (safe fallback if not installed)
run_omz() {
log_step "Starting oh-my-zsh update (${SCRIPT_NAME})"
echo
if ! command_exists omz; then
log_warning "oh-my-zsh (omz) is not installed on this system, skipping omz update"
return 0
fi
log_info "Running omz update..."
if omz update 2>&1; then
log_success "oh-my-zsh updated"
else
log_warning "omz update failed"
fi
}
# all: run update, clean, then omz in order
run_all() {
run_update
echo
run_clean
echo
run_omz
}
# Print usage information
print_usage() {
cat <<EOF
Usage: ${SCRIPT_NAME} [subcommand]
Unified system housekeeping script.
Subcommands:
update Update the OS package index (detects distro, updates correct package manager)
clean Run system cleanup (docker, package caches, temp/cache/log dirs, journal, thumbnails, memory)
omz Update oh-my-zsh (skipped safely if not installed)
all Run update, then clean, then omz (default when no subcommand is given)
If no subcommand is provided, 'all' is assumed.
EOF
}
#==============================================================================
# MAIN EXECUTION
#==============================================================================
main() {
local subcommand="${1:-all}"
case "$subcommand" in
update)
run_update
;;
clean)
run_clean
;;
omz)
run_omz
;;
all)
run_all
;;
-h|--help|help)
print_usage
;;
*)
log_warning "Unknown subcommand: '${subcommand}'"
echo
print_usage
exit 0
;;
esac
}
main "$@"

269
update.sh
View File

@@ -1,269 +0,0 @@
#!/bin/bash
# Package List Update Script
# Detects the running OS and updates the package index only (no upgrades).
set -euo pipefail
#==============================================================================
# CONFIGURATION
#==============================================================================
readonly NC='\033[0m'
readonly RED='\033[1;31m'
readonly GREEN='\033[1;32m'
readonly LIGHT_GREEN='\033[1;32m'
readonly LIGHT_BLUE='\033[1;34m'
readonly LIGHT_GREY='\033[0;37m'
readonly YELLOW='\033[1;33m'
readonly SCRIPT_NAME="$(basename "$0")"
#==============================================================================
# UTILITY FUNCTIONS
#==============================================================================
log_info() { echo -e "${LIGHT_GREY}[i] $1${NC}"; }
log_success() { echo -e "${LIGHT_GREEN}[✓] $1${NC}"; }
log_warning() { echo -e "${YELLOW}[!] $1${NC}"; }
log_error() { echo -e "${RED}[x] $1${NC}" >&2; }
log_step() { echo -e "${LIGHT_BLUE}[i] $1${NC}"; }
die() {
log_error "$1"
exit 1
}
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Detect the running OS and its ID string.
# Prints two values: the distro id (lowercase) and the family id (lowercase).
detect_os() {
local id=""
local family=""
if [[ -f /etc/os-release ]]; then
# shellcheck disable=SC1091
. /etc/os-release
id="${ID:-unknown}"
family="${ID_LIKE:-}"
elif [[ -f /etc/alpine-release ]]; then
id="alpine"
family="alpine"
else
# Fall back to package manager detection
if command_exists apk; then
id="alpine"; family="alpine"
elif command_exists pacman; then
id="arch"; family="arch"
elif command_exists apt-get; then
id="debian"; family="debian"
elif command_exists dnf; then
id="fedora"; family="rhel"
elif command_exists yum; then
id="centos"; family="rhel"
elif command_exists zypper; then
id="opensuse"; family="suse"
elif command_exists xbps-install; then
id="void"; family="void"
elif command_exists emerge; then
id="gentoo"; family="gentoo"
else
id="unknown"; family="unknown"
fi
fi
echo "${id} ${family}"
}
#==============================================================================
# OS-SPECIFIC UPDATE FUNCTIONS
#==============================================================================
update_alpine() {
if ! command_exists apk; then
log_warning "apk not found on Alpine-type system"
return 0
fi
log_info "Running apk update..."
if apk update 2>&1; then
log_success "Alpine package index updated"
else
log_warning "apk update failed"
fi
}
update_arch() {
if ! command_exists pacman; then
log_warning "pacman not found on Arch-type system"
return 0
fi
log_info "Refreshing pacman database (pacman -Sy)..."
# -S = sync, -y = refresh local database only (no package installation)
if pacman -Sy --noconfirm 2>&1; then
log_success "Arch package database refreshed"
else
log_warning "pacman -Sy failed"
fi
}
update_debian() {
if ! command_exists apt-get; then
log_warning "apt-get not found on Debian-type system"
return 0
fi
log_info "Running apt-get update..."
if apt-get update 2>&1; then
log_success "Debian/Ubuntu package index updated"
else
log_warning "apt-get update failed"
fi
}
update_rhel() {
if command_exists dnf; then
log_info "Running dnf makecache..."
if dnf makecache 2>&1; then
log_success "RHEL/Fedora metadata cache refreshed (dnf)"
else
log_warning "dnf makecache failed"
fi
elif command_exists yum; then
log_info "Running yum makecache..."
if yum makecache fast 2>&1; then
log_success "CentOS/RHEL metadata cache refreshed (yum)"
else
log_warning "yum makecache failed"
fi
else
log_warning "No DNF or YUM found on RHEL-type system"
fi
}
update_suse() {
if ! command_exists zypper; then
log_warning "zypper not found on openSUSE-type system"
return 0
fi
log_info "Running zypper refresh..."
if zypper --non-interactive refresh 2>&1; then
log_success "openSUSE repositories refreshed"
else
log_warning "zypper refresh failed"
fi
}
update_void() {
if ! command_exists xbps-install; then
log_warning "xbps-install not found on Void system"
return 0
fi
log_info "Running xbps-install -S (sync repository index)..."
if xbps-install -S 2>&1; then
log_success "Void package index synced"
else
log_warning "xbps-install -S failed"
fi
}
update_gentoo() {
if ! command_exists emerge; then
log_warning "emerge not found on Gentoo system"
return 0
fi
log_info "Running emaint sync -a..."
if emaint sync -a 2>&1; then
log_success "Gentoo Portage tree synced"
else
log_warning "emaint sync failed"
fi
}
#==============================================================================
# MAIN EXECUTION
#==============================================================================
main() {
log_step "Starting package list update (${SCRIPT_NAME})"
echo
# Detect the OS
local distro_info distro_id family_id
distro_info="$(detect_os)"
distro_id="${distro_info%% *}"
family_id="${distro_info##* }"
log_info "Detected OS: ${distro_id}"
if [[ -n "${family_id}" && "${family_id}" != "${distro_id}" ]]; then
log_info "OS family: ${family_id}"
fi
echo
# Dispatch to the correct handler based on distro family
# Check distro_id first, then fall back to family_id for aliases
case "${distro_id}" in
alpine)
update_alpine
;;
arch|artix|manjaro|garuda)
update_arch
;;
debian|ubuntu|linuxmint|kali|raspbian|pop)
update_debian
;;
fedora|rhel|centos|rocky|almalinux|amazon|oracle|clear-linux-os)
update_rhel
;;
opensuse*|suse|sles)
update_suse
;;
void)
update_void
;;
gentoo|funtoo)
update_gentoo
;;
*)
# Try matching on family ID as fallback
case "${family_id}" in
*alpine*)
update_alpine
;;
*arch*)
update_arch
;;
*debian*|*ubuntu*)
update_debian
;;
*rhel*|*fedora*|*centos*)
update_rhel
;;
*suse*|*opensuse*)
update_suse
;;
*void*)
update_void
;;
*gentoo*)
update_gentoo
;;
*)
die "Unsupported OS: ${distro_id} (${family_id}). Cannot update package index."
;;
esac
;;
esac
echo
log_success "Package list update completed!"
}
main "$@"