#!/bin/bash # ========================================== # System Cleanup Script for macOS # ========================================== # # This script helps clean up various caches and temporary files to free up disk space. # Use with caution - while this script tries to be safe, it's always good to have backups. # # Features: # - Clean development tool caches (Homebrew, npm, pip, etc.) # - Clean browser caches (Chrome, Firefox, Safari) # - Clean application caches # - Clean system caches and logs # - Interactive menu to choose what to clean # - Size reporting before and after cleaning # # Author: Saravana # Created: 2025-05-21 # ========================================== # ========================================== # Color Definitions # ========================================== RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' NC='\033[0m' # No Color # ========================================== # Helper Functions # ========================================== # Print with color print_header() { echo -e "\n${CYAN}===== $1 =====${NC}\n" } print_info() { echo -e "${BLUE}INFO: $1${NC}" } print_warning() { echo -e "${YELLOW}WARNING: $1${NC}" } print_error() { echo -e "${RED}ERROR: $1${NC}" } print_success() { echo -e "${GREEN}SUCCESS: $1${NC}" } print_size() { path="$1" if [ -e "$path" ]; then size=$(du -sh "$path" 2>/dev/null | cut -f1) echo -e "${PURPLE}Size of $path: $size${NC}" else echo -e "${PURPLE}$path does not exist${NC}" fi } # Check if a directory exists and has content check_directory() { local dir="$1" if [ -d "$dir" ] && [ "$(ls -A "$dir" 2>/dev/null)" ]; then return 0 else return 1 fi } # Get directory size in human-readable format get_dir_size() { local dir="$1" if [ -d "$dir" ]; then du -sh "$dir" 2>/dev/null | cut -f1 else echo "0B" fi } # Get directory size in bytes get_dir_size_bytes() { local dir="$1" if [ -d "$dir" ]; then du -s "$dir" 2>/dev/null | cut -f1 else echo "0" fi } # Get free disk space get_free_space() { df -h . | awk 'NR==2 {print $4}' } # Confirm action confirm() { local prompt="$1" local default="$2" if [ "$default" = "y" ]; then local prompt_text="${prompt} [Y/n] " else local prompt_text="${prompt} [y/N] " fi read -p "$prompt_text" response if [ -z "$response" ]; then response="$default" fi if [[ "$response" =~ ^[Yy]$ ]]; then return 0 else return 1 fi } # ========================================== # Cleanup Functions # ========================================== # Clean Development Tool Caches clean_dev_caches() { print_header "Cleaning Development Tool Caches" # Homebrew if check_directory "$HOME/homebrew/Caches" || check_directory "$HOME/Library/Caches/Homebrew"; then print_info "Cleaning Homebrew caches..." before_size=$(get_dir_size "$HOME/Library/Caches/Homebrew") if confirm "Run 'brew cleanup' to remove old versions and downloads? This will free up space but require re-downloading if needed again." "y"; then brew cleanup -s rm -rf "$HOME/Library/Caches/Homebrew"/* print_success "Homebrew cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Caches/Homebrew")" fi fi # Node/npm if check_directory "$HOME/.npm"; then print_info "Cleaning npm cache..." before_size=$(get_dir_size "$HOME/.npm") if confirm "Clean npm cache?" "y"; then npm cache clean --force print_success "npm cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/.npm")" fi fi # pnpm if check_directory "$HOME/.pnpm-store" || check_directory "$HOME/Library/Caches/pnpm"; then print_info "Cleaning pnpm cache..." before_size=$(get_dir_size "$HOME/.pnpm-store") before_size2=$(get_dir_size "$HOME/Library/Caches/pnpm") if confirm "Clean pnpm cache?" "y"; then if command -v pnpm &>/dev/null; then pnpm store prune fi rm -rf "$HOME/Library/Caches/pnpm"/* print_success "pnpm cache cleaned. Before: $before_size + $before_size2, After: $(get_dir_size "$HOME/.pnpm-store") + $(get_dir_size "$HOME/Library/Caches/pnpm")" fi fi # Yarn if check_directory "$HOME/.yarn/cache"; then print_info "Cleaning Yarn cache..." before_size=$(get_dir_size "$HOME/.yarn/cache") if confirm "Clean Yarn cache?" "y"; then if command -v yarn &>/dev/null; then yarn cache clean fi print_success "Yarn cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/.yarn/cache")" fi fi # pip if check_directory "$HOME/Library/Caches/pip"; then print_info "Cleaning pip cache..." before_size=$(get_dir_size "$HOME/Library/Caches/pip") if confirm "Clean pip cache?" "y"; then if command -v pip &>/dev/null; then pip cache purge fi rm -rf "$HOME/Library/Caches/pip"/* print_success "pip cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Caches/pip")" fi fi # Cypress if check_directory "$HOME/Library/Caches/Cypress"; then print_info "Cleaning Cypress cache..." before_size=$(get_dir_size "$HOME/Library/Caches/Cypress") if confirm "Clean Cypress cache? This includes browser binaries that will need to be re-downloaded." "y"; then rm -rf "$HOME/Library/Caches/Cypress"/* print_success "Cypress cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Caches/Cypress")" fi fi # Playwright if check_directory "$HOME/Library/Caches/ms-playwright"; then print_info "Cleaning Playwright browsers cache..." before_size=$(get_dir_size "$HOME/Library/Caches/ms-playwright") if confirm "Clean Playwright browsers cache? These will need to be re-downloaded if used again." "y"; then rm -rf "$HOME/Library/Caches/ms-playwright"/* print_success "Playwright cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Caches/ms-playwright")" fi fi # Gradle if check_directory "$HOME/.gradle/caches"; then print_info "Cleaning Gradle cache..." before_size=$(get_dir_size "$HOME/.gradle/caches") if confirm "Clean Gradle cache?" "y"; then rm -rf "$HOME/.gradle/caches"/* print_success "Gradle cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/.gradle/caches")" fi fi # CocoaPods if check_directory "$HOME/Library/Caches/CocoaPods"; then print_info "Cleaning CocoaPods cache..." before_size=$(get_dir_size "$HOME/Library/Caches/CocoaPods") if confirm "Clean CocoaPods cache?" "y"; then rm -rf "$HOME/Library/Caches/CocoaPods"/* print_success "CocoaPods cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Caches/CocoaPods")" fi fi # Go modules if check_directory "$HOME/go/pkg/mod"; then print_info "Cleaning Go module cache..." before_size=$(get_dir_size "$HOME/go/pkg/mod") if confirm "Clean Go module cache? These will be re-downloaded when needed." "y"; then if command -v go &>/dev/null; then go clean -modcache fi print_success "Go module cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/go/pkg/mod")" fi fi } # Clean Browser Caches clean_browser_caches() { print_header "Cleaning Browser Caches" # Check and close browsers first for browser in "Firefox" "Google Chrome" "Safari" "Microsoft Edge" "Brave Browser"; do if pgrep "$browser" >/dev/null; then print_warning "$browser is currently running." if confirm "Would you like to close $browser before cleaning its cache?" "y"; then osascript -e "tell application \"$browser\" to quit" sleep 2 else print_warning "Skipping $browser cleanup to avoid potential issues." continue fi fi case "$browser" in "Firefox") if check_directory "$HOME/Library/Caches/Firefox"; then print_info "Cleaning Firefox cache..." before_size=$(get_dir_size "$HOME/Library/Caches/Firefox") if confirm "Clean Firefox cache?" "y"; then rm -rf "$HOME/Library/Caches/Firefox"/* print_success "Firefox cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Caches/Firefox")" fi fi ;; "Google Chrome") if check_directory "$HOME/Library/Caches/Google/Chrome"; then print_info "Cleaning Chrome cache..." before_size=$(get_dir_size "$HOME/Library/Caches/Google/Chrome") if confirm "Clean Chrome cache?" "y"; then rm -rf "$HOME/Library/Caches/Google/Chrome"/* print_success "Chrome cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Caches/Google/Chrome")" fi fi ;; "Safari") if check_directory "$HOME/Library/Caches/com.apple.Safari"; then print_info "Cleaning Safari cache..." if confirm "Clean Safari cache?" "y"; then print_warning "Safari caches require administrator privileges." if confirm "Run with sudo (will prompt for password)?" "n"; then sudo rm -rf "$HOME/Library/Caches/com.apple.Safari"/* print_success "Safari cache cleaned." fi fi fi ;; "Microsoft Edge") if check_directory "$HOME/Library/Caches/Microsoft Edge"; then print_info "Cleaning Microsoft Edge cache..." before_size=$(get_dir_size "$HOME/Library/Caches/Microsoft Edge") if confirm "Clean Microsoft Edge cache?" "y"; then rm -rf "$HOME/Library/Caches/Microsoft Edge"/* print_success "Microsoft Edge cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Caches/Microsoft Edge")" fi fi ;; "Brave Browser") if check_directory "$HOME/Library/Caches/BraveSoftware/Brave-Browser"; then print_info "Cleaning Brave Browser cache..." before_size=$(get_dir_size "$HOME/Library/Caches/BraveSoftware/Brave-Browser") if confirm "Clean Brave Browser cache?" "y"; then rm -rf "$HOME/Library/Caches/BraveSoftware/Brave-Browser"/* print_success "Brave Browser cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Caches/BraveSoftware/Brave-Browser")" fi fi ;; esac done } # Clean Application Caches clean_app_caches() { print_header "Cleaning Application Caches" # Check for common application caches cache_dir="$HOME/Library/Caches" # List large application caches print_info "Scanning for large application caches..." # Get top 10 largest cache directories large_caches=$(find "$cache_dir" -type d -maxdepth 1 2>/dev/null | xargs du -sh 2>/dev/null | sort -hr | head -n 10) echo -e "${YELLOW}Largest application caches:${NC}" echo "$large_caches" echo # Spotify if check_directory "$HOME/Library/Caches/com.spotify.client"; then print_info "Cleaning Spotify cache..." before_size=$(get_dir_size "$HOME/Library/Caches/com.spotify.client") if confirm "Clean Spotify cache? This may remove offline songs." "n"; then rm -rf "$HOME/Library/Caches/com.spotify.client"/* print_success "Spotify cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Caches/com.spotify.client")" fi fi # Slack if check_directory "$HOME/Library/Caches/com.tinyspeck.slackmacgap"; then print_info "Cleaning Slack cache..." before_size=$(get_dir_size "$HOME/Library/Caches/com.tinyspeck.slackmacgap") if confirm "Clean Slack cache?" "y"; then rm -rf "$HOME/Library/Caches/com.tinyspeck.slackmacgap"/* print_success "Slack cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Caches/com.tinyspeck.slackmacgap")" fi fi # Visual Studio Code if check_directory "$HOME/Library/Caches/com.microsoft.VSCode"; then print_info "Cleaning VS Code cache..." before_size=$(get_dir_size "$HOME/Library/Caches/com.microsoft.VSCode") if confirm "Clean VS Code cache?" "y"; then rm -rf "$HOME/Library/Caches/com.microsoft.VSCode"/* print_success "VS Code cache cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Caches/com.microsoft.VSCode")" fi fi # Docker if check_directory "$HOME/Library/Containers/com.docker.docker"; then print_info "Found Docker containers cache..." before_size=$(get_dir_size "$HOME/Library/Containers/com.docker.docker") if confirm "Would you like to see Docker disk usage? (This will run 'docker system df')" "y"; then if command -v docker &>/dev/null; then docker system df if confirm "Clean unused Docker data? (This will run 'docker system prune')" "n"; then docker system prune -f print_success "Docker unused data cleaned." fi else print_error "Docker command not found." fi fi fi # Clean other large caches if confirm "Would you like to see and clean other large application caches?" "y"; then print_info "Select cache directories to clean (enter numbers separated by space):" # Get all cache directories cache_dirs=() while IFS= read -r line; do cache_dirs+=("$line") done < <(find "$cache_dir" -type d -maxdepth 1 2>/dev/null | sort) # Get sizes for each directory cache_sizes=() for dir in "${cache_dirs[@]}"; do if [ -d "$dir" ]; then size=$(du -sh "$dir" 2>/dev/null | cut -f1) cache_sizes+=("$size") else cache_sizes+=("") fi done # Display directories with their sizes for i in "${!cache_dirs[@]}"; do if [ -n "${cache_sizes[$i]}" ] && [ "${cache_dirs[$i]}" != "$cache_dir" ]; then echo "$i) ${cache_sizes[$i]} - ${cache_dirs[$i]}" fi done read -p "Enter numbers to clean (or 'all' for all, 'skip' to skip): " selections if [ "$selections" = "all" ]; then for i in "${!cache_dirs[@]}"; do if [ "${cache_dirs[$i]}" != "$cache_dir" ] && [ -d "${cache_dirs[$i]}" ]; then dir_name=$(basename "${cache_dirs[$i]}") if confirm "Clean $dir_name (${cache_sizes[$i]})?" "n"; then before_size=$(get_dir_size "${cache_dirs[$i]}") rm -rf "${cache_dirs[$i]}"/* print_success "$dir_name cache cleaned. Before: $before_size, After: $(get_dir_size "${cache_dirs[$i]}")" fi fi done elif [ "$selections" != "skip" ]; then for sel in $selections; do if [ -n "${cache_dirs[$sel]}" ] && [ "${cache_dirs[$sel]}" != "$cache_dir" ]; then dir_name=$(basename "${cache_dirs[$sel]}") before_size=$(get_dir_size "${cache_dirs[$sel]}") rm -rf "${cache_dirs[$sel]}"/* print_success "$dir_name cache cleaned. Before: $before_size, After: $(get_dir_size "${cache_dirs[$sel]}")" fi done fi fi } # Clean System Caches and Logs clean_system_caches() { print_header "Cleaning System Caches and Logs" # macOS system logs print_info "Checking system logs..." if confirm "Clean system logs? This requires administrator privileges." "n"; then print_warning "This will clear system logs. You'll be prompted for your password." sudo rm -rf /private/var/log/* print_success "System logs cleaned." fi # Temporary files print_info "Checking temporary files..." before_size=$(get_dir_size /tmp) if confirm "Clean temporary files in /tmp?" "y"; then rm -rf /tmp/* 2>/dev/null print_success "Temporary files cleaned. Before: $before_size, After: $(get_dir_size /tmp)" fi # User cache print_info "Checking user cache..." before_size=$(get_dir_size "$HOME/Library/Caches") if confirm "Clean all user caches? (This is aggressive and may remove important cached data)" "n"; then find "$HOME/Library/Caches" -depth 1 -not -name "." -not -name ".." -exec rm -rf {} \; 2>/dev/null print_success "User caches cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Caches")" fi # Clear DNS cache if confirm "Clear DNS cache?" "y"; then sudo dscacheutil -flushcache sudo killall -HUP mDNSResponder print_success "DNS cache flushed." fi # Application logs if check_directory "$HOME/Library/Logs"; then print_info "Checking application logs..." before_size=$(get_dir_size "$HOME/Library/Logs") if confirm "Clean application logs?" "y"; then find "$HOME/Library/Logs" -type f -delete 2>/dev/null print_success "Application logs cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Logs")" fi fi # XCode derived data if check_directory "$HOME/Library/Developer/Xcode/DerivedData"; then print_info "Checking XCode derived data..." before_size=$(get_dir_size "$HOME/Library/Developer/Xcode/DerivedData") if confirm "Clean XCode derived data? This is safe but will require rebuilding projects." "y"; then rm -rf "$HOME/Library/Developer/Xcode/DerivedData"/* print_success "XCode derived data cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Developer/Xcode/DerivedData")" fi fi # iOS Device Support files if check_directory "$HOME/Library/Developer/Xcode/iOS DeviceSupport"; then print_info "Checking iOS Device Support files..." before_size=$(get_dir_size "$HOME/Library/Developer/Xcode/iOS DeviceSupport") if confirm "Clean iOS Device Support files? These will be recreated when you connect iOS devices." "y"; then rm -rf "$HOME/Library/Developer/Xcode/iOS DeviceSupport"/* print_success "iOS Device Support files cleaned. Before: $before_size, After: $(get_dir_size "$HOME/Library/Developer/Xcode/iOS DeviceSupport")" fi fi # macOS trash print_info "Checking trash bin..." if [ -d "$HOME/.Trash" ]; then before_size=$(get_dir_size "$HOME/.Trash") if [ "$before_size" != "0B" ]; then if confirm "Empty trash bin? This action cannot be undone." "n"; then rm -rf "$HOME/.Trash"/* print_success "Trash emptied. Before: $before_size, After: $(get_dir_size "$HOME/.Trash")" fi else print_info "Trash is already empty." fi fi # Software update downloads if check_directory "/Library/Updates"; then if confirm "Clean software update downloads? This requires administrator privileges." "n"; then print_warning "This will clear downloaded software updates. You'll be prompted for your password." sudo rm -rf /Library/Updates/* print_success "Software update downloads cleaned." fi fi } # Show disk usage summary show_disk_usage() { print_header "Disk Usage Summary" # System disk usage echo -e "${CYAN}System Disk Usage:${NC}" df -h / | awk 'NR==1 || NR==2 {print}' echo # User directory sizes echo -e "${CYAN}User Directory Sizes:${NC}" du -h -d 1 ~ 2>/dev/null | sort -hr | head -n 10 echo # Large directories echo -e "${CYAN}Large Directories:${NC}" du -h -d 2 ~ 2>/dev/null | sort -hr | head -n 5 echo # Development caches echo -e "${CYAN}Development Tool Caches:${NC}" print_size "$HOME/Library/Caches/Homebrew" print_size "$HOME/.npm" print_size "$HOME/Library/Caches/pip" print_size "$HOME/Library/Caches/Cypress" print_size "$HOME/Library/Caches/pnpm" print_size "$HOME/Library/Caches/ms-playwright" echo # Browser caches echo -e "${CYAN}Browser Caches:${NC}" print_size "$HOME/Library/Caches/Google/Chrome" print_size "$HOME/Library/Caches/Firefox" print_size "$HOME/Library/Caches/com.apple.Safari" print_size "$HOME/Library/Caches/Microsoft Edge" print_size "$HOME/Library/Caches/BraveSoftware/Brave-Browser" echo } # Main menu main_menu() { local initial_free_space=$(get_free_space) # Initial disk usage report clear print_header "MacOS Cleanup Utility" echo -e "Current free space: ${GREEN}$initial_free_space${NC}" echo while true; do echo -e "${CYAN}Please select an option:${NC}" echo "1) Show disk usage summary" echo "2) Clean development tool caches" echo "3) Clean browser caches" echo "4) Clean application caches" echo "5) Clean system caches and logs" echo "6) Run all cleanups" echo "7) Exit" echo read -p "Enter your choice (1-7): " choice echo case $choice in 1) show_disk_usage ;; 2) clean_dev_caches ;; 3) clean_browser_caches ;; 4) clean_app_caches ;; 5) clean_system_caches ;; 6) print_header "Running All Cleanups" print_warning "This will run all cleanup options. You'll be prompted for each action." if confirm "Do you want to continue?" "n"; then clean_dev_caches clean_browser_caches clean_app_caches clean_system_caches fi ;; 7) break ;; *) print_error "Invalid choice. Please enter a number between 1 and 7." ;; esac echo echo -e "Press Enter to return to the menu..." read clear print_header "MacOS Cleanup Utility" echo -e "Current free space: ${GREEN}$(get_free_space)${NC} (was ${BLUE}$initial_free_space${NC})" echo done # Show final summary local final_free_space=$(get_free_space) print_header "Cleanup Complete" echo -e "Initial free space: ${BLUE}$initial_free_space${NC}" echo -e "Final free space: ${GREEN}$final_free_space${NC}" # Calculate space saved local initial_bytes=$(echo "$initial_free_space" | sed 's/G//g') local final_bytes=$(echo "$final_free_space" | sed 's/G//g') if (( $(echo "$final_bytes > $initial_bytes" | bc -l) )); then local saved=$(echo "$final_bytes - $initial_bytes" | bc -l) echo -e "You freed approximately ${GREEN}${saved}GB${NC} of disk space!" fi echo print_info "Thank you for using the MacOS Cleanup Utility!" echo } # Run main menu main_menu