Linux 目录自动清理脚本

文章目录[隐藏]

背景

在日常运维和开发工作中,日志文件、缓存文件或临时文件往往不断积累,如果不及时清理,不仅占用大量磁盘空间,还可能影响系统性能甚至导致磁盘满载的问题。手动清理繁琐且易出错,所以有了这个自动化脚本。

主要功能

  • 指定目录和天数参数化
  • 删除文件后同时清理空目录,保证目录结构整洁
  • 日志输出支持 ANSI 彩色提示,方便日志浏览和诊断。
  • Dry-run 模式,提供 -d / --dry-run 参数,只显示即将删除的文件列表而不执行删除,方便先行检查
  • 易用的帮助文档,无参数执行打印操作文档

脚本内容

#!/bin/bash

# ============================================================
# 清理旧文件脚本
# 功能:删除指定目录下超过30天未修改的文件
# 用法:./cleanup_old_files.sh <目录路径> [天数]
# 示例:./cleanup_old_files.sh /var/log/myapp 30
# 
# 定时任务配置示例 (crontab -e):
#   每天凌晨3点执行:
#   0 3 * * * /path/to/cleanup_old_files.sh /var/log/myapp 30 >> /var/log/cleanup.log 2>&1
# ============================================================

# 默认配置
DEFAULT_DAYS=30
LOG_PREFIX="[cleanup_old_files]"

# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# 日志函数
log_info() {
    echo -e "${GREEN}${LOG_PREFIX} [INFO] $(date '+%Y-%m-%d %H:%M:%S') $1${NC}"
}

log_warn() {
    echo -e "${YELLOW}${LOG_PREFIX} [WARN] $(date '+%Y-%m-%d %H:%M:%S') $1${NC}"
}

log_error() {
    echo -e "${RED}${LOG_PREFIX} [ERROR] $(date '+%Y-%m-%d %H:%M:%S') $1${NC}"
}

# 格式化文件大小
format_size() {
    local size=$1
    if [[ $size -ge 1073741824 ]]; then
        awk "BEGIN {printf \"%.2fGB\", $size/1073741824}"
    elif [[ $size -ge 1048576 ]]; then
        awk "BEGIN {printf \"%.2fMB\", $size/1048576}"
    elif [[ $size -ge 1024 ]]; then
        awk "BEGIN {printf \"%.2fKB\", $size/1024}"
    else
        echo "${size}B"
    fi
}

# 显示帮助
show_help() {
    echo "用法: $0 <目录路径> [天数]"
    echo ""
    echo "参数:"
    echo "  目录路径    必填,要清理的目录路径"
    echo "  天数        可选,删除多少天前的文件(默认: ${DEFAULT_DAYS})"
    echo ""
    echo "选项:"
    echo "  -h, --help     显示此帮助信息"
    echo "  -d, --dry-run  模拟运行,只显示要删除的文件,不实际删除"
    echo ""
    echo "示例:"
    echo "  $0 /var/log/myapp           # 删除30天前的文件"
    echo "  $0 /var/log/myapp 7         # 删除7天前的文件"
    echo "  $0 /var/log/myapp 30 -d     # 模拟运行,显示30天前要删除的文件"
    echo ""
    echo "定时任务配置 (crontab -e):"
    echo "  # 每天凌晨3点清理"
    echo "  0 3 * * * /path/to/cleanup_old_files.sh /var/log/myapp 30 >> /var/log/cleanup.log 2>&1"
}

# 检查参数
DRY_RUN=false
TARGET_DIR=""
DAYS=$DEFAULT_DAYS

# 解析参数
while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help)
            show_help
            exit 0
            ;;
        -d|--dry-run)
            DRY_RUN=true
            shift
            ;;
        *)
            if [[ -z "$TARGET_DIR" ]]; then
                TARGET_DIR="$1"
            elif [[ "$1" =~ ^[0-9]+$ ]]; then
                DAYS="$1"
            else
                log_error "未知参数: $1"
                show_help
                exit 1
            fi
            shift
            ;;
    esac
done

# 验证目录参数
if [[ -z "$TARGET_DIR" ]]; then
    log_error "请提供目录路径"
    show_help
    exit 1
fi

# 检查目录是否存在
if [[ ! -d "$TARGET_DIR" ]]; then
    log_error "目录不存在: $TARGET_DIR"
    exit 1
fi

# 获取绝对路径
TARGET_DIR=$(cd "$TARGET_DIR" && pwd)

log_info "开始清理任务"
log_info "目标目录: $TARGET_DIR"
log_info "删除 ${DAYS} 天前的文件"
if [[ "$DRY_RUN" == "true" ]]; then
    log_warn "模拟运行模式 - 不会实际删除文件"
fi

# 创建临时文件列表
TMP_FILE_LIST=$(mktemp)
trap "rm -f $TMP_FILE_LIST" EXIT

log_info "正在搜索超过 ${DAYS} 天未修改的文件..."

# 先找出所有文件并保存到临时文件
find "$TARGET_DIR" -type f -mtime +${DAYS} > "$TMP_FILE_LIST" 2>/dev/null || true

TOTAL_FILES=$(wc -l < "$TMP_FILE_LIST" | tr -d ' ')
log_info "找到 ${TOTAL_FILES} 个文件待处理"

if [[ $TOTAL_FILES -eq 0 ]]; then
    log_info "没有找到需要删除的文件"
    log_info "============ 清理完成 ============"
    exit 0
fi

# 统计信息
TOTAL_SIZE=0
DELETED_COUNT=0
FAILED_COUNT=0
CURRENT=0

# 逐个处理文件
while IFS= read -r file; do
    CURRENT=$((CURRENT + 1))

    if [[ -f "$file" ]]; then
        # 获取文件大小
        FILE_SIZE=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null || echo 0)
        TOTAL_SIZE=$((TOTAL_SIZE + FILE_SIZE))
        FILE_SIZE_STR=$(format_size $FILE_SIZE)

        if [[ "$DRY_RUN" == "true" ]]; then
            log_info "[${CURRENT}/${TOTAL_FILES}] [模拟] 将删除: $file ($FILE_SIZE_STR)"
            DELETED_COUNT=$((DELETED_COUNT + 1))
        else
            if rm -f "$file" 2>/dev/null; then
                log_info "[${CURRENT}/${TOTAL_FILES}] 已删除: $file ($FILE_SIZE_STR)"
                DELETED_COUNT=$((DELETED_COUNT + 1))
            else
                log_error "[${CURRENT}/${TOTAL_FILES}] 删除失败: $file"
                FAILED_COUNT=$((FAILED_COUNT + 1))
            fi
        fi
    fi
done < "$TMP_FILE_LIST"

# 清理空目录
log_info "正在清理空目录..."
EMPTY_DIRS=0

while IFS= read -r dir; do
    if [[ "$DRY_RUN" == "true" ]]; then
        log_info "[模拟] 将删除空目录: $dir"
        EMPTY_DIRS=$((EMPTY_DIRS + 1))
    else
        if rmdir "$dir" 2>/dev/null; then
            log_info "已删除空目录: $dir"
            EMPTY_DIRS=$((EMPTY_DIRS + 1))
        fi
    fi
done < <(find "$TARGET_DIR" -type d -empty 2>/dev/null || true)

# 输出统计信息
echo ""
log_info "============ 清理完成 ============"
log_info "找到的文件数: $TOTAL_FILES"
log_info "删除的文件数: $DELETED_COUNT"
if [[ $FAILED_COUNT -gt 0 ]]; then
    log_warn "删除失败数: $FAILED_COUNT"
fi
log_info "释放空间: $(format_size $TOTAL_SIZE)"
log_info "清理的空目录数: $EMPTY_DIRS"
log_info "=================================="

exit 0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注