编写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
相关产品推荐
相关产品推荐

