#!/bin/bash # Log and GZ File Cleaner Script # Removes all .log and .gz files from specified directories # Function to display usage show_usage() { echo "Usage: $0 [OPTIONS] [directory2] [directory3] ..." echo "" echo "Options:" echo " -r, --recursive Clean files recursively in subdirectories (DEFAULT)" echo " -s, --surface-only Only clean files in specified directory (no subdirs)" echo " -i, --interactive Ask before deleting each file" echo " -v, --verbose Show detailed output" echo " -n, --dry-run Show what would be deleted without actually deleting" echo " -f, --force Force deletion without prompts" echo " -o, --old-only DAYS Only delete files older than specified days" echo " -z, --size-only SIZE Only delete files larger than specified size (e.g., 10M, 1G)" echo " -h, --help Show this help message" echo "" echo "Examples:" echo " $0 /var/log # Clean all subdirectories" echo " $0 --surface-only /var/log # Clean only /var/log (no subdirs)" echo " $0 --dry-run /var/log # Show what would be deleted" echo " $0 --old-only 7 /var/log # Only files older than 7 days" echo " $0 --size-only 100M /var/log # Only files larger than 100MB" echo "" echo "File types cleaned: *.log, *.gz" } # Default options RECURSIVE=true INTERACTIVE=false VERBOSE=false DRY_RUN=false FORCE=false OLD_ONLY_DAYS="" SIZE_ONLY="" DIRECTORIES=() # Counters TOTAL_FILES=0 TOTAL_SIZE=0 DELETED_FILES=0 DELETED_SIZE=0 # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in -r|--recursive) RECURSIVE=true shift ;; -s|--surface-only) RECURSIVE=false shift ;; -i|--interactive) INTERACTIVE=true shift ;; -v|--verbose) VERBOSE=true shift ;; -n|--dry-run) DRY_RUN=true shift ;; -f|--force) FORCE=true shift ;; -o|--old-only) OLD_ONLY_DAYS="$2" shift 2 ;; -z|--size-only) SIZE_ONLY="$2" shift 2 ;; -h|--help) show_usage exit 0 ;; -*) echo "Unknown option: $1" show_usage exit 1 ;; *) DIRECTORIES+=("$1") shift ;; esac done # Check if directories are provided if [ ${#DIRECTORIES[@]} -eq 0 ]; then echo "Error: At least one directory must be specified" show_usage exit 1 fi # Validate old-only days parameter if [ -n "$OLD_ONLY_DAYS" ] && ! [[ "$OLD_ONLY_DAYS" =~ ^[0-9]+$ ]]; then echo "Error: --old-only parameter must be a positive number" exit 1 fi # Function to convert size to bytes size_to_bytes() { local size="$1" local num=$(echo "$size" | sed 's/[^0-9]//g') local unit=$(echo "$size" | sed 's/[0-9]//g' | tr '[:lower:]' '[:upper:]') # Convert to integer (remove decimal part if present) num=${num%%.*} case "$unit" in "K"|"KB") echo $((num * 1024)) ;; "M"|"MB") echo $((num * 1024 * 1024)) ;; "G"|"GB") echo $((num * 1024 * 1024 * 1024)) ;; "") echo "$num" ;; *) echo "0" ;; esac } # Function to format file size format_size() { local bytes=$1 if [ $bytes -lt 1024 ]; then echo "${bytes}B" elif [ $bytes -lt 1048576 ]; then echo "$((bytes / 1024))K" elif [ $bytes -lt 1073741824 ]; then echo "$((bytes / 1048576))M" else echo "$((bytes / 1073741824))G" fi } # Function to check if file meets criteria meets_criteria() { local file="$1" local file_size=$(stat -c%s "$file" 2>/dev/null || echo "0") local file_age_days="" # Check age criteria if [ -n "$OLD_ONLY_DAYS" ]; then file_age_days=$(find "$file" -mtime +$OLD_ONLY_DAYS -print | wc -l) if [ "$file_age_days" -eq 0 ]; then return 1 fi fi # Check size criteria if [ -n "$SIZE_ONLY" ]; then local min_size=$(size_to_bytes "$SIZE_ONLY") if [ "$file_size" -lt "$min_size" ]; then return 1 fi fi return 0 } # Function to process a single file process_file() { local file="$1" local file_size=$(stat -c%s "$file" 2>/dev/null || echo "0") local formatted_size=$(format_size $file_size) ((TOTAL_FILES++)) TOTAL_SIZE=$((TOTAL_SIZE + file_size)) # Check if file meets criteria if ! meets_criteria "$file"; then if [ "$VERBOSE" = true ]; then echo " SKIP: $file (doesn't meet criteria)" fi return fi if [ "$DRY_RUN" = true ]; then echo " WOULD DELETE: $file ($formatted_size)" ((DELETED_FILES++)) DELETED_SIZE=$((DELETED_SIZE + file_size)) return fi # Interactive mode if [ "$INTERACTIVE" = true ] && [ "$FORCE" = false ]; then echo -n "Delete $file ($formatted_size)? [y/N] " read -r response if [[ ! "$response" =~ ^[Yy]$ ]]; then if [ "$VERBOSE" = true ]; then echo " SKIP: $file (user declined)" fi return fi fi # Delete the file if rm -f "$file" 2>/dev/null; then if [ "$VERBOSE" = true ]; then echo " DELETED: $file ($formatted_size)" fi ((DELETED_FILES++)) DELETED_SIZE=$((DELETED_SIZE + file_size)) else echo " ERROR: Failed to delete $file" fi } # Function to clean a directory clean_directory() { local dir="$1" if [ ! -d "$dir" ]; then echo "Warning: Directory '$dir' does not exist" return fi if [ ! -r "$dir" ]; then echo "Warning: Directory '$dir' is not readable" return fi echo "Cleaning directory: $dir" # Build find command (always recursive by default) local find_cmd="find \"$dir\" -type f \\( -name \"*.log\" -o -name \"*.gz\" \\)" # Only add maxdepth if surface-only mode is explicitly requested if [ "$RECURSIVE" = false ]; then find_cmd="find \"$dir\" -maxdepth 1 -type f \\( -name \"*.log\" -o -name \"*.gz\" \\)" fi # Execute find and process files local files=() while IFS= read -r -d '' file; do files+=("$file") done < <(eval "$find_cmd -print0" 2>/dev/null) if [ ${#files[@]} -eq 0 ]; then echo " No log or gz files found" return fi echo " Found ${#files[@]} files to process" # Process each file for file in "${files[@]}"; do process_file "$file" done } # Main execution echo "Log and GZ File Cleaner" echo "=======================" if [ "$DRY_RUN" = true ]; then echo "DRY RUN MODE - No files will be deleted" fi if [ -n "$OLD_ONLY_DAYS" ]; then echo "Only processing files older than $OLD_ONLY_DAYS days" fi if [ -n "$SIZE_ONLY" ]; then echo "Only processing files larger than $SIZE_ONLY" fi if [ "$RECURSIVE" = true ]; then echo "Recursive mode enabled (cleaning all subdirectories)" else echo "Surface-only mode (cleaning specified directories only)" fi echo "" # Process each directory for dir in "${DIRECTORIES[@]}"; do clean_directory "$dir" echo "" done # Summary echo "Summary:" echo "========" echo "Total files found: $TOTAL_FILES" echo "Total size found: $(format_size $TOTAL_SIZE)" if [ "$DRY_RUN" = true ]; then echo "Files that would be deleted: $DELETED_FILES" echo "Space that would be freed: $(format_size $DELETED_SIZE)" else echo "Files deleted: $DELETED_FILES" echo "Space freed: $(format_size $DELETED_SIZE)" fi if [ "$DELETED_FILES" -gt 0 ] || [ "$DRY_RUN" = true ]; then exit 0 else exit 1 fi