You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

编写Bash脚本删除早于X天的备份文件夹,保留最新N个

解决Bash备份脚本的旧备份删除需求

核心需求回顾

删除早于backup_keepdays天的备份文件夹,但至少保留最新的min_n_backups个备份,同时支持含空格、换行等特殊字符的路径,适配Ubuntu 22.04等现代Linux系统。

使用参数:

target_basefolder="/path/to/backups/parent/folder"
min_n_backups=3
backup_keepdays=28

版本A的问题解决(单行命令优化)

你之前的问题是tail不支持NUL分隔符,但GNU tail(Ubuntu 22.04默认是GNU版本)提供了-z选项来处理NUL分隔的输入/输出。直接修改版本A即可实现保留最新N个、删除其余的需求:

# 按文件名逆序排序(如果文件名含YYYYMMDD时间戳,此方式有效)
find "$target_basefolder" -maxdepth 1 -type d -not -path "$target_basefolder" -print0 |
    sort -rz |
    tail -z -n +$((min_n_backups + 1)) |
    xargs -r0 rm -rf

如果需要按修改时间排序(更可靠,不依赖文件名格式),可以结合find的时间戳输出:

find "$target_basefolder" -maxdepth 1 -type d -not -path "$target_basefolder" -printf '%T@ %p\0' |
    sort -zrn |  # 按修改时间戳逆序排序
    awk -v RS='\0' -v n="$min_n_backups" 'NR > n {sub(/^[0-9.]+ /, ""); print}' |
    xargs -r0 rm -rf

版本B的逻辑修正

版本B的核心问题是逻辑错误:当符合天数要求的备份不足N个时,应该保留最新的N个,而非不删除任何旧备份。修正后的脚本:

# 统计不早于backup_keepdays天的备份数量
n_recent=$(find "$target_basefolder" -maxdepth 1 -type d -not -path "$target_basefolder" -ctime -"$backup_keepdays" -printf '.' | wc -m)

if (( n_recent >= min_n_backups )); then
    # 当最近备份数量足够N个时,仅删除早于backup_keepdays天的备份
    find "$target_basefolder" -maxdepth 1 -type d -not -path "$target_basefolder" -ctime +"$backup_keepdays" -print0 |
        xargs -r0 rm -rf
else
    # 当最近备份不足N个时,保留最新的min_n_backups个,删除其余
    find "$target_basefolder" -maxdepth 1 -type d -not -path "$target_basefolder" -printf '%T@ %p\0' |
        sort -zrn |
        awk -v RS='\0' -v n="$min_n_backups" 'NR > n {sub(/^[0-9.]+ /, ""); print}' |
        xargs -r0 rm -rf
fi

版本C的循环优化

原版本C的for循环无法处理NUL分隔的列表,改用while read -d ''来正确处理特殊字符路径,同时简化逻辑:

icount=1
# 按修改时间逆序输出备份文件夹(时间戳+路径)
find "$target_basefolder" -maxdepth 1 -type d -not -path "$target_basefolder" -printf '%T@ %p\0' |
    sort -zrn |
    # 逐行读取NUL分隔的内容
    while IFS= read -d '' -r line; do
        # 分离时间戳和路径
        ifolder="${line#* }"
        # 检查是否早于backup_keepdays天
        is_old=$(find "$ifolder" -maxdepth 0 -type d -ctime +"$backup_keepdays" -printf '.' | wc -m)
        # 仅删除:序号超过N 且 早于指定天数的备份
        if (( icount > min_n_backups && is_old > 0 )); then
            rm -rf "$ifolder"
        fi
        ((icount++))
    done

最优综合方案(兼顾天数和数量)

如果需要严格满足「保留所有不早于X天的备份 + 若数量不足则补充最新的至N个」,可以用comm命令对比两个保留列表,最终删除不在保留集合内的备份:

# 生成临时文件,存储需要保留的列表
tmp_dir=$(mktemp -d)
keep_top_n="$tmp_dir/keep_top_n.txt"
keep_recent="$tmp_dir/keep_recent.txt"
keep_final="$tmp_dir/keep_final.txt"

# 1. 生成最新N个备份的列表(按修改时间排序)
find "$target_basefolder" -maxdepth 1 -type d -not -path "$target_basefolder" -printf '%T@ %p\0' |
    sort -zrn |
    awk -v RS='\0' -v n="$min_n_backups" 'NR <= n {sub(/^[0-9.]+ /, ""); print}' |
    sort -z > "$keep_top_n"

# 2. 生成不早于backup_keepdays天的备份列表
find "$target_basefolder" -maxdepth 1 -type d -not -path "$target_basefolder" -ctime -"$backup_keepdays" -print0 |
    sort -z > "$keep_recent"

# 3. 取两个列表的并集(最终需要保留的备份)
comm -z -12 "$keep_top_n" "$keep_recent" | cat "$keep_top_n" - | sort -z | uniq -z > "$keep_final"

# 4. 删除所有不在保留列表内的备份
find "$target_basefolder" -maxdepth 1 -type d -not -path "$target_basefolder" -print0 |
    sort -z |
    comm -z -23 - "$keep_final" |
    xargs -r0 rm -rf

# 清理临时文件
rm -rf "$tmp_dir"

内容的提问来源于stack exchange,提问作者smatberg_gpe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 07:55:58