From d4182561217567b8f352b1af0a73aca335c15724 Mon Sep 17 00:00:00 2001 From: ashishsinghbora <135435891+ashishsinghbora@users.noreply.github.com> Date: Thu, 24 Sep 2026 23:33:05 +0530 Subject: [PATCH] fix(maintenance): harden cache cleanup against broad deletion and improve file organizer safety --- scripts/maintenance/cleanup-cache.sh | 163 +++++++++++++++++++++----- scripts/maintenance/file-organizer.sh | 71 ++++++++--- 2 files changed, 184 insertions(+), 50 deletions(-) diff --git a/scripts/maintenance/cleanup-cache.sh b/scripts/maintenance/cleanup-cache.sh index 67a3419..eb58c38 100755 --- a/scripts/maintenance/cleanup-cache.sh +++ b/scripts/maintenance/cleanup-cache.sh @@ -1,52 +1,153 @@ #!/usr/bin/env bash - set -euo pipefail show_help() { - cat <<'EOF' -Usage: $(basename "$0") [OPTIONS] - -Clean temporary files and display cache information. + cat <<'EOF' +Usage: cleanup-cache.sh [options] Options: - -h Show this help message + -h, --help Show this help message and exit + -n, --dry-run Show cache sizes and actions without deleting anything + -y, --yes Automatically confirm cleanup without prompting + +Description: + Inspects and cleans user cache directories (thumbnail cache, trash) + and reports package manager / system journal cache usage with + recommended maintenance commands. + Never deletes critical system sockets or running process locks. EOF + exit 0 } -if [[ "${1-}" == "-h" ]]; then - show_help - exit 0 +DRY_RUN=false +AUTOYES=false + +while (( "$#" )); do + case "$1" in + -h|--help) + show_help + ;; + -n|--dry-run) + DRY_RUN=true + ;; + -y|--yes) + AUTOYES=true + ;; + -*) + echo "Error: Unknown option: $1" >&2 + exit 1 + ;; + *) + echo "Error: Unexpected argument: $1" >&2 + exit 1 + ;; + esac + shift +done + +echo "=== Cache & Temporary Files Inspector ===" +echo "" + +# 1. User thumbnail cache +THUMB_CACHE="${HOME:-}/.cache/thumbnails" +if [[ -d "$THUMB_CACHE" ]]; then + THUMB_SIZE="$(du -sh "$THUMB_CACHE" 2>/dev/null | cut -f1 || echo "0B")" + echo "User thumbnail cache: $THUMB_CACHE (Size: $THUMB_SIZE)" + if $DRY_RUN; then + echo "[DRY RUN] Would clean thumbnail files in $THUMB_CACHE" + elif $AUTOYES; then + find "$THUMB_CACHE" -type f -delete 2>/dev/null || true + echo " Cleaned thumbnail cache." + else + read -r -p "Clean user thumbnail cache? [y/N] " resp + case "$resp" in + y|Y|yes|YES) + find "$THUMB_CACHE" -type f -delete 2>/dev/null || true + echo " Cleaned thumbnail cache." + ;; + *) + echo " Skipped thumbnail cache." + ;; + esac + fi +else + echo "User thumbnail cache: None found." fi -echo "Cleaning temporary files..." +# 2. User Trash +TRASH_DIR="${HOME:-}/.local/share/Trash" +if [[ -d "$TRASH_DIR" ]]; then + TRASH_SIZE="$(du -sh "$TRASH_DIR" 2>/dev/null | cut -f1 || echo "0B")" + echo "User trash: $TRASH_DIR (Size: $TRASH_SIZE)" + if $DRY_RUN; then + echo "[DRY RUN] Would empty user trash in $TRASH_DIR" + elif $AUTOYES; then + rm -rf "${TRASH_DIR:?}"/files/* "${TRASH_DIR:?}"/info/* 2>/dev/null || true + echo " Emptied user trash." + else + read -r -p "Empty user trash? [y/N] " resp + case "$resp" in + y|Y|yes|YES) + rm -rf "${TRASH_DIR:?}"/files/* "${TRASH_DIR:?}"/info/* 2>/dev/null || true + echo " Emptied user trash." + ;; + *) + echo " Skipped user trash." + ;; + esac + fi +fi + +# 3. Systemd journal +echo "" +if command -v journalctl >/dev/null 2>&1; then + echo "Systemd Journal:" + journalctl --disk-usage 2>/dev/null || true + echo " (To vacuum logs older than 7 days: sudo journalctl --vacuum-time=7d)" +fi -if [[ -d /tmp ]]; then - find /tmp -mindepth 1 -maxdepth 1 -user "$USER" -exec rm -rf -- {} + +# 4. Package Manager Cache +echo "" +echo "Package Manager Cache Status:" +if command -v pacman >/dev/null 2>&1; then + if [[ -d /var/cache/pacman/pkg ]]; then + PAC_SIZE="$(du -sh /var/cache/pacman/pkg 2>/dev/null | cut -f1 || echo "unknown")" + echo " Pacman cache size: $PAC_SIZE (/var/cache/pacman/pkg)" + echo " Recommended: 'sudo paccache -r' or 'sudo pacman -Sc'" + fi fi -if command -v pacman >dev/null 2>&1; then - echo - echo "Pacman cache:" - du -sh /var/cache/pacman/pkg 2>/dev/null || true - echo - echo "Use 'sudo paccache -r' to safely remove old package versions." +if command -v apt-get >/dev/null 2>&1 || command -v apt >/dev/null 2>&1; then + if [[ -d /var/cache/apt/archives ]]; then + APT_SIZE="$(du -sh /var/cache/apt/archives 2>/dev/null | cut -f1 || echo "unknown")" + echo " APT cache size: $APT_SIZE (/var/cache/apt/archives)" + echo " Recommended: 'sudo apt-get clean' or 'sudo apt-get autoclean'" + fi fi -echo -echo "Cleanup completed." -echo "Cleaning temporary files..." +if command -v dnf >/dev/null 2>&1; then + if [[ -d /var/cache/dnf ]]; then + DNF_SIZE="$(du -sh /var/cache/dnf 2>/dev/null | cut -f1 || echo "unknown")" + echo " DNF cache size: $DNF_SIZE (/var/cache/dnf)" + echo " Recommended: 'sudo dnf clean all'" + fi +fi -if [[ -d /tmp ]]; then - find /tmp -mindepth 1 -maxdepth 1 -user "$USER" -exec rm -rf -- {} + +if command -v zypper >/dev/null 2>&1; then + if [[ -d /var/cache/zypp ]]; then + ZYPP_SIZE="$(du -sh /var/cache/zypp 2>/dev/null | cut -f1 || echo "unknown")" + echo " Zypper cache size: $ZYPP_SIZE (/var/cache/zypp)" + echo " Recommended: 'sudo zypper clean'" + fi fi -if command -v pacman >/dev/null 2>&1; then - echo - echo "Pacman cache:" - du -sh /var/cache/pacman/pkg 2>/dev/null || true - echo - echo "Use 'sudo paccache -r' to safely remove old package versions." +if command -v apk >/dev/null 2>&1; then + if [[ -d /var/cache/apk ]]; then + APK_SIZE="$(du -sh /var/cache/apk 2>/dev/null | cut -f1 || echo "unknown")" + echo " APK cache size: $APK_SIZE (/var/cache/apk)" + echo " Recommended: 'sudo apk cache clean'" + fi fi -echo -echo "Cleanup completed." +echo "" +echo "Cache inspection completed." diff --git a/scripts/maintenance/file-organizer.sh b/scripts/maintenance/file-organizer.sh index 620ccfa..832ba6c 100755 --- a/scripts/maintenance/file-organizer.sh +++ b/scripts/maintenance/file-organizer.sh @@ -1,5 +1,4 @@ #!/usr/bin/env bash - set -euo pipefail show_help() { @@ -14,9 +13,10 @@ Options: Description: Safely organizes unorganized loose files in a directory into subfolders based on their file extensions (Images, Documents, Archives, Audio, Video, Code). - - Skips directories, hidden files (starting with .), and scripts. - - Safely handles filenames with spaces. + - Skips directories, symlinks, hidden files (starting with .), and files without extensions. + - Safely handles filenames with spaces, special characters, and Unicode. - Safe dry-run mode enabled via -n / --dry-run. + - Prevents accidental execution directly on root '/' or home directory. EOF exit 0 } @@ -26,14 +26,28 @@ DRY_RUN=false while (( "$#" )); do case "$1" in - -h|--help) show_help;; - -n|--dry-run) DRY_RUN=true;; + -h|--help) + show_help + ;; + -n|--dry-run) + DRY_RUN=true + ;; -d|--dir) - shift - TARGET_DIR="${1:-.}" + if [[ -n "${2-}" && "${2-}" != -* ]]; then + TARGET_DIR="$2" + shift + else + echo "Error: Argument for $1 is missing" >&2 + exit 1 + fi + ;; + -*) + echo "Error: Unknown option: $1" >&2 + exit 1 + ;; + *) + TARGET_DIR="$1" ;; - -*) echo "Unknown option: $1" >&2; exit 1;; - *) TARGET_DIR="$1";; esac shift done @@ -43,7 +57,20 @@ if [[ ! -d "$TARGET_DIR" ]]; then exit 1 fi -TARGET_DIR="$(cd "$TARGET_DIR" && pwd)" +TARGET_DIR="$(cd "$TARGET_DIR" && pwd -P)" + +# Prevent accidental execution on / or $HOME directly +if [[ "$TARGET_DIR" == "/" ]]; then + echo "Error: Refusing to organize root directory '/'." >&2 + exit 1 +fi + +if [[ -n "${HOME:-}" && "$TARGET_DIR" == "$HOME" ]]; then + echo "Error: Refusing to organize user home directory root '$HOME' directly." >&2 + echo "Please specify a subfolder (e.g. ~/Downloads or ~/Desktop)." >&2 + exit 1 +fi + echo "=== File Organizer ===" echo "Target directory: $TARGET_DIR" if $DRY_RUN; then @@ -54,20 +81,24 @@ echo "" get_category() { local ext="${1,,}" case "$ext" in - jpg|jpeg|png|gif|bmp|svg|webp|tiff) echo "Images" ;; - pdf|doc|docx|txt|rtf|odt|xls|xlsx|ppt|pptx|csv) echo "Documents" ;; - zip|tar|gz|bz2|xz|7z|rar) echo "Archives" ;; - mp3|wav|ogg|flac|aac|m4a) echo "Audio" ;; - mp4|mkv|avi|mov|wmv|flv|webm) echo "Video" ;; - py|js|ts|c|cpp|h|hpp|go|rs|java|html|css|json|yaml|yml|sh) echo "Code" ;; + jpg|jpeg|png|gif|bmp|svg|webp|tiff|ico) echo "Images" ;; + pdf|doc|docx|txt|rtf|odt|xls|xlsx|ppt|pptx|csv|md) echo "Documents" ;; + zip|tar|gz|bz2|xz|7z|rar|tgz|tbz2|txz) echo "Archives" ;; + mp3|wav|ogg|flac|aac|m4a|wma) echo "Audio" ;; + mp4|mkv|avi|mov|wmv|flv|webm|m4v) echo "Video" ;; + py|js|ts|c|cpp|h|hpp|go|rs|java|html|css|json|yaml|yml|sh|bash) echo "Code" ;; *) echo "" ;; esac } moved_count=0 +skipped_count=0 -# Iterate over regular files in TARGET_DIR (non-recursive, ignore hidden files) +# Iterate over regular files in TARGET_DIR (non-recursive, skip symlinks and hidden files) while IFS= read -r -d '' file; do + # Skip symlinks to avoid broken relative links or dangling targets + [[ -L "$file" ]] && continue + filename="$(basename "$file")" # Skip hidden files [[ "$filename" == .* ]] && continue @@ -86,10 +117,12 @@ while IFS= read -r -d '' file; do if $DRY_RUN; then echo "[DRY RUN] Would move '$filename' -> '$category/'" + moved_count=$((moved_count + 1)) else mkdir -p "$dest_dir" if [[ -e "$dest_file" ]]; then echo "Skipping '$filename': already exists in '$category/'" + skipped_count=$((skipped_count + 1)) else mv -- "$file" "$dest_file" echo "Moved '$filename' -> '$category/'" @@ -101,7 +134,7 @@ done < <(find "$TARGET_DIR" -maxdepth 1 -type f -print0) echo "" if $DRY_RUN; then - echo "Dry run completed." + echo "Dry run completed. Files that would be moved: $moved_count" else - echo "Organization complete. Files moved: $moved_count" + echo "Organization complete. Files moved: $moved_count, skipped: $skipped_count" fi