Last active
April 27, 2026 11:58
-
-
Save gil00pita/9b00b35f7058a1e2c0592415484774bd to your computer and use it in GitHub Desktop.
Space cleanup in Linux
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # ============================================================================== | |
| # cleanup.sh — Linux System Deep Cleanup Script | |
| # Version : 3.0 | |
| # ============================================================================== | |
| # | |
| # DESCRIPTION | |
| # ─────────── | |
| # A single-file Bash script that safely removes caches, logs, temp files, | |
| # and build artefacts from common developer tools on any Linux machine. | |
| # Each section is self-contained — if a tool is not installed or a folder | |
| # does not exist the section is silently skipped and the script continues. | |
| # | |
| # USAGE | |
| # ───── | |
| # 1. Download / copy this script to your machine: | |
| # curl -O https://your-host/cleanup.sh # or copy manually | |
| # | |
| # 2. Make it executable (one-time): | |
| # chmod +x cleanup.sh | |
| # | |
| # 3. Run it (sudo is required for system-level cleanups): | |
| # sudo ./cleanup.sh | |
| # | |
| # Or, if you prefer not to run the whole script as root, the script will | |
| # automatically re-invoke itself with sudo when needed. | |
| # | |
| # WHAT IT CLEANS (17 sections) | |
| # ────────────────────────────── | |
| # § 1 Docker | |
| # • Stopped containers (docker container prune) | |
| # • Dangling / unused images (docker image prune) | |
| # • Unused volumes (docker volume prune) | |
| # • Unused networks (docker network prune) | |
| # • Build cache (docker system prune -af --volumes) | |
| # | |
| # § 2 npm | |
| # • HTTP / package cache (~/.npm or custom cache dir) | |
| # | |
| # § 3 Yarn | |
| # • Package cache (~/.yarn/cache) | |
| # | |
| # § 4 pnpm | |
| # • Orphaned store packages (pnpm store prune) | |
| # | |
| # § 5 pip / pip3 | |
| # • Wheel & HTTP download cache (~/.cache/pip) | |
| # | |
| # § 6 apt / dnf / yum | |
| # • Downloaded .deb/.rpm files (/var/cache/apt, /var/cache/dnf) | |
| # • Autoremoveable dependencies | |
| # | |
| # § 7 systemd journal | |
| # • Log entries older than 7 days or beyond 100 MB | |
| # | |
| # § 8 /tmp and /var/tmp | |
| # • Files in /tmp not accessed in 7+ days | |
| # • Files in /var/tmp not accessed in 30+ days | |
| # | |
| # § 9 User caches & browser data | |
| # • Thumbnail cache (~/.cache/thumbnails) | |
| # • Chrome cache (~/.cache/google-chrome/…/Cache) | |
| # • Firefox cache (~/.cache/mozilla) | |
| # • Opera cache (~/.cache/opera) | |
| # • Brave cache (~/.cache/BraveSoftware) | |
| # • Vivaldi cache (~/.cache/vivaldi) | |
| # • Desktop Trash (~/.local/share/Trash) | |
| # | |
| # § 10 Snap | |
| # • Old / disabled snap revisions | |
| # | |
| # § 11 Flatpak | |
| # • Unused runtimes (flatpak uninstall --unused) | |
| # | |
| # § 12 Old kernel images [Debian/Ubuntu only] | |
| # • Previous kernel packages (apt autoremove --purge) | |
| # | |
| # § 13 Core dump files | |
| # • core and core.<PID> files found anywhere on disk | |
| # | |
| # § 14 VS Code | |
| # • GPU / shader cache (~/.config/Code/GPUCache) | |
| # • HTTP & code cache (~/.config/Code/Cache, Code Cache) | |
| # • Cached extension VSIXs (~/.config/Code/CachedExtensionVSIXs) | |
| # • Crash reports (~/.config/Code/Crashpad) | |
| # • Internal service logs (~/.config/Code/logs) | |
| # • Hot-exit backups (~/.config/Code/Backups) | |
| # • Old remote server binaries (~/.vscode-server/bin) | |
| # • workspaceStorage >30 days (~/.config/Code/User/workspaceStorage) | |
| # | |
| # § 15 Cursor (AI code editor — VS Code fork) | |
| # • Same cache layout as VS Code under ~/.config/Cursor | |
| # • Extra AI scratch dirs (~/.cursor/cache, logs, tmp) | |
| # • workspaceStorage >30 days (~/.config/Cursor/User/workspaceStorage) | |
| # | |
| # § 16 OpenAI Codex CLI | |
| # • Local response cache (~/.codex/cache) | |
| # • Logs (~/.codex/logs) | |
| # • Temp files (~/.codex/tmp) | |
| # • OpenAI SDK HTTP cache (~/.cache/openai) | |
| # • Session JSON files >30 days (~/.codex/sessions) | |
| # | |
| # § 17 Claude (Anthropic desktop app & Claude Code CLI) | |
| # • Electron GPU / shader cache (~/.config/Claude/GPUCache) | |
| # • Electron HTTP cache (~/.config/Claude/Cache, Code Cache) | |
| # • Crash reports (~/.config/Claude/Crashpad) | |
| # • App logs (~/.config/Claude/logs) | |
| # • CLI cache / logs / tmp (~/.claude/cache, logs, tmp) | |
| # • Generic XDG cache buckets (~/.cache/claude, ~/.cache/anthropic) | |
| # • Conversation logs >30 days (~/.claude/conversations, projects) | |
| # | |
| # WHAT IT DOES NOT TOUCH | |
| # ────────────────────── | |
| # • Your VS Code / Cursor settings, keybindings, or installed extensions | |
| # • Claude Code / Codex conversation history newer than 30 days | |
| # • Any database, source-code, or project files | |
| # • Files owned by other users (runs as the invoking user's $HOME) | |
| # | |
| # NOTES | |
| # ───── | |
| # • Safe to run repeatedly — idempotent, nothing breaks if already clean. | |
| # • Each section prints ✔ (cleaned), ↷ (skipped), or ⚠ (warning). | |
| # • A space-freed summary is printed at the end. | |
| # • Tested on Ubuntu 22.04 / 24.04, Debian 12, Fedora 39, Arch Linux. | |
| # | |
| # ============================================================================== | |
| # -u : error on unset variables | |
| # -o pipefail : catch pipe failures | |
| # NOTE: -e (exit-on-error) is intentionally OMITTED — a missing path or | |
| # unavailable tool must never abort the whole script. Every risky | |
| # operation is guarded with an existence check or || true. | |
| set -uo pipefail | |
| # ── Colours ─────────────────────────────────────────────────────────────────── | |
| RED='\033[0;31m'; YELLOW='\033[1;33m'; GREEN='\033[0;32m' | |
| CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m' | |
| # ── Helpers ─────────────────────────────────────────────────────────────────── | |
| info() { echo -e "${CYAN}▸ $*${RESET}"; } | |
| success() { echo -e "${GREEN}✔ $*${RESET}"; } | |
| warn() { echo -e "${YELLOW}⚠ $*${RESET}"; } | |
| header() { echo -e "\n${BOLD}━━━ $* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"; } | |
| skip() { echo -e "${YELLOW} ↷ skipped — $*${RESET}"; } | |
| has() { command -v "$1" &>/dev/null; } | |
| # dir_size PATH — human-readable size, or "0 B" if path absent | |
| dir_size() { | |
| local p="$1" | |
| [[ -e "$p" ]] && du -sh "$p" 2>/dev/null | awk '{print $1}' || echo "0 B" | |
| } | |
| # safe_rm PATH [LABEL] | |
| # Removes a file or directory if it exists; silently skips if not found; | |
| # never aborts the script on permission or other errors. | |
| safe_rm() { | |
| local target="$1" | |
| local label="${2:-$(basename "$target")}" | |
| if [[ -d "$target" ]]; then | |
| local sz; sz=$(dir_size "$target") | |
| info "Removing ${label} (${sz})…" | |
| rm -rf "${target:?}" 2>/dev/null || warn "Could not fully remove ${label} — continuing." | |
| elif [[ -f "$target" ]]; then | |
| info "Removing file: ${label}…" | |
| rm -f "${target:?}" 2>/dev/null || warn "Could not remove ${label} — continuing." | |
| else | |
| skip "${label} not found." | |
| fi | |
| } | |
| # safe_find_delete BASE LABEL [find options…] | |
| # Runs find inside BASE (if it exists) and deletes matches; skips if BASE absent. | |
| safe_find_delete() { | |
| local base="$1" label="$2"; shift 2 | |
| if [[ -d "$base" ]]; then | |
| info "Pruning ${label}…" | |
| find "$base" "$@" -delete 2>/dev/null || true | |
| else | |
| skip "${label} base directory not found." | |
| fi | |
| } | |
| # clean_dirs LABEL DIR [DIR …] | |
| # Calls safe_rm on each DIR; prints a single success line at the end if any | |
| # directory was found, or a single skip line if none were. | |
| clean_dirs() { | |
| local label="$1"; shift | |
| local found=0 | |
| for d in "$@"; do | |
| if [[ -d "$d" ]]; then | |
| safe_rm "$d" "$(basename "$d")" | |
| found=1 | |
| else | |
| skip "$(basename "$d") not found." | |
| fi | |
| done | |
| if [[ $found -eq 1 ]]; then | |
| success "${label} cleaned." | |
| else | |
| skip "${label} — nothing to clean." | |
| fi | |
| } | |
| # ── Root check ──────────────────────────────────────────────────────────────── | |
| if [[ $EUID -ne 0 ]]; then | |
| warn "Some cleanups require root. Re-running with sudo…" | |
| exec sudo bash "$0" "$@" | |
| fi | |
| echo -e "\n${BOLD}${CYAN}╔══════════════════════════════════════════╗" | |
| echo -e "║ 🧹 Linux System Cleanup ║" | |
| echo -e "╚══════════════════════════════════════════╝${RESET}" | |
| echo -e " Started: $(date '+%Y-%m-%d %H:%M:%S')\n" | |
| TOTAL_BEFORE=$(df / --output=used -BM 2>/dev/null | tail -1 | tr -d 'M' || echo 0) | |
| START_TIME=$(date '+%Y-%m-%d %H:%M:%S') | |
| # Identify the actual human user (not root when sudo'd) | |
| REAL_USER="${SUDO_USER:-${USER:-root}}" | |
| REAL_HOME=$(getent passwd "$REAL_USER" 2>/dev/null | cut -d: -f6 || echo "/root") | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "1 · Docker" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| if has docker && docker info &>/dev/null 2>&1; then | |
| info "Removing stopped containers…" | |
| docker container prune -f 2>/dev/null || warn "docker container prune failed — skipping." | |
| info "Removing dangling images…" | |
| docker image prune -f 2>/dev/null || warn "docker image prune failed — skipping." | |
| info "Removing unused volumes…" | |
| docker volume prune -f 2>/dev/null || warn "docker volume prune failed — skipping." | |
| info "Removing unused networks…" | |
| docker network prune -f 2>/dev/null || warn "docker network prune failed — skipping." | |
| info "Full system prune (unused images, build cache)…" | |
| docker system prune -af --volumes 2>/dev/null || warn "docker system prune failed — skipping." | |
| success "Docker cleaned." | |
| elif has docker; then | |
| skip "Docker installed but daemon is not running." | |
| else | |
| skip "Docker not installed." | |
| fi | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "2 · npm" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| if has npm; then | |
| NPM_CACHE=$(npm config get cache 2>/dev/null || echo "${REAL_HOME}/.npm") | |
| info "npm cache: ${NPM_CACHE} ($(dir_size "$NPM_CACHE"))" | |
| npm cache clean --force 2>/dev/null || warn "npm cache clean failed — skipping." | |
| success "npm cache cleared." | |
| else | |
| skip "npm not installed." | |
| fi | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "3 · Yarn" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| if has yarn; then | |
| YARN_CACHE=$(yarn cache dir 2>/dev/null || echo "${REAL_HOME}/.yarn/cache") | |
| info "Yarn cache: ${YARN_CACHE} ($(dir_size "$YARN_CACHE"))" | |
| yarn cache clean --all 2>/dev/null || yarn cache clean 2>/dev/null || warn "yarn cache clean failed — skipping." | |
| success "Yarn cache cleared." | |
| else | |
| skip "Yarn not installed." | |
| fi | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "4 · pnpm" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| if has pnpm; then | |
| info "Pruning pnpm store…" | |
| pnpm store prune 2>/dev/null || warn "pnpm store prune failed — skipping." | |
| success "pnpm store pruned." | |
| else | |
| skip "pnpm not installed." | |
| fi | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "5 · pip / pip3" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| PIP_CMD="" | |
| has pip3 && PIP_CMD="pip3" | |
| has pip && PIP_CMD="${PIP_CMD:-pip}" | |
| if [[ -n "$PIP_CMD" ]]; then | |
| PIP_CACHE=$($PIP_CMD cache dir 2>/dev/null || echo "${REAL_HOME}/.cache/pip") | |
| info "pip cache: ${PIP_CACHE} ($(dir_size "$PIP_CACHE"))" | |
| $PIP_CMD cache purge 2>/dev/null || safe_rm "$PIP_CACHE" "pip cache" | |
| success "pip cache cleared." | |
| else | |
| skip "pip not installed." | |
| fi | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "6 · apt / dpkg" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| if has apt-get; then | |
| info "apt autoremove…" | |
| apt-get autoremove -y -qq 2>/dev/null || warn "apt autoremove failed — skipping." | |
| info "apt autoclean…" | |
| apt-get autoclean -y -qq 2>/dev/null || warn "apt autoclean failed — skipping." | |
| info "apt clean (package cache)…" | |
| apt-get clean 2>/dev/null || warn "apt clean failed — skipping." | |
| success "apt cleaned." | |
| else | |
| skip "apt not available (not a Debian/Ubuntu system)." | |
| fi | |
| # dnf / yum (Fedora / RHEL) | |
| if has dnf; then | |
| info "dnf autoremove + clean…" | |
| dnf autoremove -y -q 2>/dev/null || warn "dnf autoremove failed — skipping." | |
| dnf clean all -q 2>/dev/null || warn "dnf clean failed — skipping." | |
| success "dnf cleaned." | |
| elif has yum; then | |
| info "yum autoremove + clean…" | |
| yum autoremove -y -q 2>/dev/null || warn "yum autoremove failed — skipping." | |
| yum clean all -q 2>/dev/null || warn "yum clean failed — skipping." | |
| success "yum cleaned." | |
| fi | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "7 · systemd journal logs" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| if has journalctl; then | |
| JOURNAL_SIZE=$(journalctl --disk-usage 2>/dev/null | awk '{print $NF}' | head -1 || echo "unknown") | |
| info "Journal size before: ${JOURNAL_SIZE}" | |
| journalctl --vacuum-time=7d --vacuum-size=100M 2>/dev/null || warn "journalctl vacuum failed — skipping." | |
| success "Journal vacuumed (kept last 7 days / ≤100 MB)." | |
| else | |
| skip "journalctl not found." | |
| fi | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "8 · /tmp and /var/tmp" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| if [[ -d /tmp ]]; then | |
| info "Removing files in /tmp older than 7 days…" | |
| find /tmp -mindepth 1 -maxdepth 2 -atime +7 -delete 2>/dev/null || true | |
| success "/tmp pruned." | |
| else | |
| skip "/tmp not found." | |
| fi | |
| if [[ -d /var/tmp ]]; then | |
| info "Removing files in /var/tmp older than 30 days…" | |
| find /var/tmp -mindepth 1 -maxdepth 2 -atime +30 -delete 2>/dev/null || true | |
| success "/var/tmp pruned." | |
| else | |
| skip "/var/tmp not found." | |
| fi | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "9 · User thumbnail & browser caches" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| safe_rm "${REAL_HOME}/.cache/thumbnails" "thumbnail cache" | |
| for CACHE_DIR in \ | |
| "${REAL_HOME}/.cache/google-chrome/Default/Cache" \ | |
| "${REAL_HOME}/.cache/mozilla" \ | |
| "${REAL_HOME}/.cache/opera" \ | |
| "${REAL_HOME}/.cache/BraveSoftware" \ | |
| "${REAL_HOME}/.cache/vivaldi" \ | |
| "${REAL_HOME}/.local/share/Trash" | |
| do | |
| safe_rm "$CACHE_DIR" | |
| done | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "10 · Snap (if installed)" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| if has snap; then | |
| info "Removing old/disabled snap revisions…" | |
| snap list --all 2>/dev/null \ | |
| | awk '/disabled/{print $1, $3}' \ | |
| | while read -r snapname revision; do | |
| snap remove "$snapname" --revision="$revision" 2>/dev/null || true | |
| done | |
| success "Old snap revisions removed." | |
| else | |
| skip "snap not installed." | |
| fi | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "11 · Flatpak (if installed)" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| if has flatpak; then | |
| info "Removing unused Flatpak runtimes…" | |
| flatpak uninstall --unused -y 2>/dev/null || warn "flatpak uninstall failed — skipping." | |
| success "Unused Flatpak runtimes removed." | |
| else | |
| skip "flatpak not installed." | |
| fi | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "12 · Old kernel images (Debian/Ubuntu only)" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| if has dpkg && has uname; then | |
| CURRENT_KERNEL=$(uname -r) | |
| info "Running kernel: ${CURRENT_KERNEL}. Running autoremove to drop old kernels…" | |
| apt-get autoremove --purge -y -qq 2>/dev/null || warn "apt autoremove --purge failed — skipping." | |
| success "Old kernels handled by autoremove." | |
| else | |
| skip "dpkg/uname not found — skipping kernel cleanup." | |
| fi | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "13 · Core dump files" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| info "Searching for core dumps…" | |
| CORE_COUNT=$(find / -xdev -maxdepth 6 \( -name 'core' -o -name 'core.[0-9]*' \) 2>/dev/null | wc -l || echo 0) | |
| if [[ "$CORE_COUNT" -gt 0 ]]; then | |
| find / -xdev -maxdepth 6 \( -name 'core' -o -name 'core.[0-9]*' \) -delete 2>/dev/null || true | |
| success "Removed ${CORE_COUNT} core dump file(s)." | |
| else | |
| skip "No core dumps found." | |
| fi | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "14 · VS Code" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| VSCODE_FOUND=0 | |
| for D in \ | |
| "${REAL_HOME}/.config/Code/Cache" \ | |
| "${REAL_HOME}/.config/Code/CachedData" \ | |
| "${REAL_HOME}/.config/Code/CachedExtensionVSIXs" \ | |
| "${REAL_HOME}/.config/Code/Code Cache" \ | |
| "${REAL_HOME}/.config/Code/GPUCache" \ | |
| "${REAL_HOME}/.config/Code/Crashpad" \ | |
| "${REAL_HOME}/.config/Code/logs" \ | |
| "${REAL_HOME}/.config/Code/Backups" \ | |
| "${REAL_HOME}/.vscode-server/bin" \ | |
| "${REAL_HOME}/.vscode-server/extensionHostProcess" | |
| do | |
| if [[ -d "$D" ]]; then | |
| safe_rm "$D" "VS Code/$(basename "$D")" | |
| VSCODE_FOUND=1 | |
| else | |
| skip "VS Code/$(basename "$D") not found." | |
| fi | |
| done | |
| safe_find_delete \ | |
| "${REAL_HOME}/.config/Code/User/workspaceStorage" \ | |
| "VS Code workspaceStorage (>30 days)" \ | |
| -mindepth 1 -maxdepth 1 -type d -atime +30 | |
| [[ $VSCODE_FOUND -eq 1 ]] && success "VS Code caches/logs cleared." || skip "VS Code — no data found." | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "15 · Cursor (AI code editor)" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| CURSOR_FOUND=0 | |
| for D in \ | |
| "${REAL_HOME}/.config/Cursor/Cache" \ | |
| "${REAL_HOME}/.config/Cursor/CachedData" \ | |
| "${REAL_HOME}/.config/Cursor/CachedExtensionVSIXs" \ | |
| "${REAL_HOME}/.config/Cursor/Code Cache" \ | |
| "${REAL_HOME}/.config/Cursor/GPUCache" \ | |
| "${REAL_HOME}/.config/Cursor/Crashpad" \ | |
| "${REAL_HOME}/.config/Cursor/logs" \ | |
| "${REAL_HOME}/.config/Cursor/Backups" \ | |
| "${REAL_HOME}/.cursor/cache" \ | |
| "${REAL_HOME}/.cursor/logs" \ | |
| "${REAL_HOME}/.cursor/tmp" | |
| do | |
| if [[ -d "$D" ]]; then | |
| safe_rm "$D" "Cursor/$(basename "$D")" | |
| CURSOR_FOUND=1 | |
| else | |
| skip "Cursor/$(basename "$D") not found." | |
| fi | |
| done | |
| safe_find_delete \ | |
| "${REAL_HOME}/.config/Cursor/User/workspaceStorage" \ | |
| "Cursor workspaceStorage (>30 days)" \ | |
| -mindepth 1 -maxdepth 1 -type d -atime +30 | |
| [[ $CURSOR_FOUND -eq 1 ]] && success "Cursor caches/logs cleared." || skip "Cursor — no data found." | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "16 · OpenAI Codex / Codex CLI" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| CODEX_FOUND=0 | |
| for D in \ | |
| "${REAL_HOME}/.codex/cache" \ | |
| "${REAL_HOME}/.codex/logs" \ | |
| "${REAL_HOME}/.codex/tmp" \ | |
| "${REAL_HOME}/.cache/openai" | |
| do | |
| if [[ -d "$D" ]]; then | |
| safe_rm "$D" "Codex/$(basename "$D")" | |
| CODEX_FOUND=1 | |
| else | |
| skip "Codex/$(basename "$D") not found." | |
| fi | |
| done | |
| safe_find_delete \ | |
| "${REAL_HOME}/.codex/sessions" \ | |
| "Codex sessions (>30 days)" \ | |
| -type f -atime +30 | |
| [[ $CODEX_FOUND -eq 1 ]] && success "Codex caches/logs cleared." || skip "Codex — no data found." | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| header "17 · Claude (Anthropic desktop / CLI)" | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| CLAUDE_FOUND=0 | |
| for D in \ | |
| "${REAL_HOME}/.config/Claude/Cache" \ | |
| "${REAL_HOME}/.config/Claude/Code Cache" \ | |
| "${REAL_HOME}/.config/Claude/GPUCache" \ | |
| "${REAL_HOME}/.config/Claude/Crashpad" \ | |
| "${REAL_HOME}/.config/Claude/logs" \ | |
| "${REAL_HOME}/.claude/cache" \ | |
| "${REAL_HOME}/.claude/logs" \ | |
| "${REAL_HOME}/.claude/tmp" \ | |
| "${REAL_HOME}/.cache/claude" \ | |
| "${REAL_HOME}/.cache/anthropic" | |
| do | |
| if [[ -d "$D" ]]; then | |
| safe_rm "$D" "Claude/$(basename "$D")" | |
| CLAUDE_FOUND=1 | |
| else | |
| skip "Claude/$(basename "$D") not found." | |
| fi | |
| done | |
| for CONV_DIR in \ | |
| "${REAL_HOME}/.claude/conversations" \ | |
| "${REAL_HOME}/.claude/projects" | |
| do | |
| safe_find_delete "$CONV_DIR" \ | |
| "Claude $(basename "$CONV_DIR") logs (>30 days)" \ | |
| -type f \( -name '*.jsonl' -o -name '*.json' \) -atime +30 | |
| done | |
| [[ $CLAUDE_FOUND -eq 1 ]] && success "Claude caches/logs cleared." || skip "Claude — no data found." | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Summary — disk usage report | |
| # ══════════════════════════════════════════════════════════════════════════════ | |
| # Capture post-clean stats | |
| TOTAL_AFTER=$(df / --output=used -BM 2>/dev/null | tail -1 | tr -d 'M' || echo 0) | |
| TOTAL_AVAIL=$(df / --output=avail -BM 2>/dev/null | tail -1 | tr -d 'M' || echo 0) | |
| TOTAL_SIZE=$(df / --output=size -BM 2>/dev/null | tail -1 | tr -d 'M' || echo 0) | |
| FREED=$(( TOTAL_BEFORE - TOTAL_AFTER )) | |
| USED_PCT=0 | |
| [[ $TOTAL_SIZE -gt 0 ]] && USED_PCT=$(( TOTAL_AFTER * 100 / TOTAL_SIZE )) | |
| FREE_PCT=$(( 100 - USED_PCT )) | |
| # Human-readable helpers (MB → GB when >= 1024) | |
| # Uses only bash integer arithmetic — no awk/bc quoting issues. | |
| fmt_mb() { | |
| local mb="$1" | |
| if [[ $mb -ge 1024 ]]; then | |
| local whole=$(( mb / 1024 )) | |
| local frac=$(( (mb % 1024) * 10 / 1024 )) | |
| echo "${whole}.${frac} GB" | |
| else | |
| echo "${mb} MB" | |
| fi | |
| } | |
| # Build a simple ASCII bar (width = 40 chars) | |
| disk_bar() { | |
| local pct="$1" # 0-100 | |
| local width=40 | |
| local filled=$(( pct * width / 100 )) | |
| local empty=$(( width - filled )) | |
| local bar="" | |
| local i | |
| for (( i=0; i<filled; i++ )); do bar+="█"; done | |
| for (( i=0; i<empty; i++ )); do bar+="░"; done | |
| echo "$bar" | |
| } | |
| echo -e "\n${BOLD}${GREEN}╔══════════════════════════════════════════════════════╗" | |
| echo -e "║ 🎉 All done! ║" | |
| echo -e "╚══════════════════════════════════════════════════════╝${RESET}" | |
| echo "" | |
| # ── Timing ──────────────────────────────────────────────────────────────────── | |
| echo -e " ${BOLD}Started :${RESET} ${START_TIME}" | |
| echo -e " ${BOLD}Finished :${RESET} $(date '+%Y-%m-%d %H:%M:%S')" | |
| echo "" | |
| # ── Space savings ───────────────────────────────────────────────────────────── | |
| echo -e " ${BOLD}── Disk Space Report ─────────────────────────────────${RESET}" | |
| echo -e " ${BOLD}Before clean :${RESET} $(fmt_mb $TOTAL_BEFORE) used" | |
| echo -e " ${BOLD}After clean :${RESET} $(fmt_mb $TOTAL_AFTER) used" | |
| echo "" | |
| if [[ $FREED -gt 0 ]]; then | |
| echo -e " ${GREEN}${BOLD} ✦ Space freed : $(fmt_mb $FREED)${RESET}" | |
| else | |
| echo -e " ${YELLOW} Space freed : < 1 MB (already clean, or filesystem rounding)${RESET}" | |
| fi | |
| echo "" | |
| # ── Current disk state ──────────────────────────────────────────────────────── | |
| BAR=$(disk_bar "$USED_PCT") | |
| # Colour the bar: green <70%, yellow <90%, red ≥90% | |
| if [[ $USED_PCT -ge 90 ]]; then | |
| BAR_COLOR="$RED" | |
| elif [[ $USED_PCT -ge 70 ]]; then | |
| BAR_COLOR="$YELLOW" | |
| else | |
| BAR_COLOR="$GREEN" | |
| fi | |
| echo -e " ${BOLD}── / Partition ───────────────────────────────────────${RESET}" | |
| echo -e " ${BAR_COLOR}${BAR}${RESET} ${USED_PCT}% used" | |
| echo -e " Used : $(fmt_mb $TOTAL_AFTER) of $(fmt_mb $TOTAL_SIZE)" | |
| echo -e " Free : $(fmt_mb $TOTAL_AVAIL)" | |
| echo "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment