Skip to content

Instantly share code, notes, and snippets.

@petenelson
Created September 19, 2025 22:13
Show Gist options
  • Save petenelson/18af8599e63090c031e47f6cb0bb4eb1 to your computer and use it in GitHub Desktop.
Save petenelson/18af8599e63090c031e47f6cb0bb4eb1 to your computer and use it in GitHub Desktop.
Delete directories that start with YYYY-MM-DD with dates earlier than today
#!/bin/bash
# Script to delete directories with date prefixes older than current date
# Usage: ./cleanup_dated_dirs.sh [--dry-run] [directory_path]
# Default values
DRY_RUN=false
TARGET_DIR="."
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--dry-run)
DRY_RUN=true
shift
;;
-h|--help)
echo "Usage: $0 [--dry-run] [directory_path]"
echo ""
echo "Options:"
echo " --dry-run Show what would be deleted without actually deleting"
echo " -h, --help Show this help message"
echo ""
echo "Arguments:"
echo " directory_path Directory to scan (default: current directory)"
echo ""
echo "Example:"
echo " $0 --dry-run /path/to/logs"
exit 0
;;
*)
TARGET_DIR="$1"
shift
;;
esac
done
# Check if target directory exists
if [[ ! -d "$TARGET_DIR" ]]; then
echo "Error: Directory '$TARGET_DIR' does not exist"
exit 1
fi
# Get current date in YYYY-MM-DD format
CURRENT_DATE=$(date +%Y-%m-%d)
echo "Current date: $CURRENT_DATE"
echo "Target directory: $TARGET_DIR"
if [[ "$DRY_RUN" == true ]]; then
echo "*** DRY RUN MODE - No directories will be deleted ***"
fi
echo ""
# Counters
deleted_count=0
skipped_count=0
# Function to check if a string is a valid date in YYYY-MM-DD format
is_valid_date() {
local date_str="$1"
echo " Validating date: '$date_str'"
# Check format with regex
if [[ ! "$date_str" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
echo " Date format invalid (not YYYY-MM-DD)"
return 1
fi
echo " Date format valid, checking if date exists..."
# Extract year, month, day
local year="${date_str:0:4}"
local month="${date_str:5:2}"
local day="${date_str:8:2}"
echo " Parsed: year=$year, month=$month, day=$day"
# Basic range checks
if [[ $month -lt 1 || $month -gt 12 ]]; then
echo " Invalid month: $month"
return 1
fi
if [[ $day -lt 1 || $day -gt 31 ]]; then
echo " Invalid day: $day"
return 1
fi
# Try multiple date command formats for compatibility
local date_error=""
# Try GNU date format first
if date -d "$date_str" >/dev/null 2>&1; then
echo " Date is valid (GNU date)"
return 0
fi
# Try BSD date format
if date -j -f "%Y-%m-%d" "$date_str" >/dev/null 2>&1; then
echo " Date is valid (BSD date)"
return 0
fi
# If both fail, capture the error for debugging
date_error=$(date -d "$date_str" 2>&1)
echo " Date validation failed. Error: $date_error"
# For basic validation, if month and day are in reasonable ranges, accept it
# This is a fallback for systems with problematic date commands
if [[ $month -ge 1 && $month -le 12 && $day -ge 1 && $day -le 31 ]]; then
echo " Using fallback validation - date appears valid"
return 0
fi
return 1
}
# Function to compare dates (returns 0 if date1 < date2)
is_date_older() {
local date1="$1"
local date2="$2"
echo " Comparing dates: '$date1' vs '$date2'"
# Try GNU date first
local date1_epoch=$(date -d "$date1" +%s 2>/dev/null)
local date2_epoch=$(date -d "$date2" +%s 2>/dev/null)
# If GNU date failed, try BSD date
if [[ -z "$date1_epoch" ]]; then
date1_epoch=$(date -j -f "%Y-%m-%d" "$date1" +%s 2>/dev/null)
fi
if [[ -z "$date2_epoch" ]]; then
date2_epoch=$(date -j -f "%Y-%m-%d" "$date2" +%s 2>/dev/null)
fi
# If date commands still fail, do string comparison as fallback
if [[ -z "$date1_epoch" || -z "$date2_epoch" ]]; then
echo " Using string comparison fallback"
if [[ "$date1" < "$date2" ]]; then
echo " $date1 is older than $date2 (string comparison)"
return 0
else
echo " $date1 is not older than $date2 (string comparison)"
return 1
fi
fi
echo " Epochs: $date1_epoch vs $date2_epoch"
if [[ $date1_epoch -lt $date2_epoch ]]; then
echo " $date1 is older than $date2"
return 0 # date1 is older
else
echo " $date1 is not older than $date2"
return 1 # date1 is not older
fi
}
# Process directories
for dir in "$TARGET_DIR"/*/; do
# Remove trailing slash and get just the directory name
dir_path="${dir%/}"
dir_name=$(basename "$dir_path")
# Debug output
echo "Processing: '$dir_name'"
# Skip if not a directory (shouldn't happen with */ glob, but be safe)
if [[ ! -d "$dir_path" ]]; then
continue
fi
# Extract potential date prefix (first 10 characters)
if [[ ${#dir_name} -ge 10 ]]; then
potential_date="${dir_name:0:10}"
echo " Checking first 10 chars: '$potential_date'"
else
potential_date=""
echo " Directory name too short for date prefix"
fi
# Check if directory name starts with a valid date
if [[ -n "$potential_date" ]] && is_valid_date "$potential_date"; then
echo " Found valid dated directory: $dir_name (date: $potential_date)"
# Check if the date is older than current date
if is_date_older "$potential_date" "$CURRENT_DATE"; then
if [[ "$DRY_RUN" == true ]]; then
echo " [DRY RUN] Would delete: $dir_path"
else
echo " Deleting: $dir_path"
if rm -rf "$dir_path"; then
echo " Successfully deleted: $dir_path"
else
echo " Error: Failed to delete $dir_path"
continue
fi
fi
((deleted_count++))
else
echo " Skipping (date is current or future): $dir_path"
((skipped_count++))
fi
else
echo " Skipping non-dated directory: $dir_name"
((skipped_count++))
fi
done
# Summary
echo ""
echo "=== Summary ==="
if [[ "$DRY_RUN" == true ]]; then
echo "Directories that would be deleted: $deleted_count"
else
echo "Directories deleted: $deleted_count"
fi
echo "Directories skipped: $skipped_count"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment