我需要每天备份此服务器上的数据和配置文件。我需要保留:
- 每周每日备份
- 每周备份一个月
- 一年内每月备份
- 此后每年备份
所有这些都是通过每天从 cron 运行的 shell 脚本完成的。
运行 10 年后备份文件应如下所示:
blog-20050103.tar.bz2
blog-20060102.tar.bz2
blog-20070101.tar.bz2
blog-20080107.tar.bz2
blog-20090105.tar.bz2
blog-20100104.tar.bz2
blog-20110103.tar.bz2
blog-20120102.tar.bz2
blog-20130107.tar.bz2
blog-20130902.tar.bz2
blog-20131007.tar.bz2
blog-20131104.tar.bz2
blog-20131202.tar.bz2
blog-20140106.tar.bz2
blog-20140203.tar.bz2
blog-20140303.tar.bz2
blog-20140407.tar.bz2
blog-20140505.tar.bz2
blog-20140602.tar.bz2
blog-20140707.tar.bz2
blog-20140728.tar.bz2
blog-20140804.tar.bz2
blog-20140811.tar.bz2
blog-20140816.tar.bz2
blog-20140817.tar.bz2
blog-20140818.tar.bz2
blog-20140819.tar.bz2
blog-20140820.tar.bz2
blog-20140821.tar.bz2
blog-20140822.tar.bz2
答案1
你真的过度设计了,太糟糕了。
以下是一些伪代码:
- 每天:
- 进行备份,放入
daily
目录 - 删除除最后 7 个
daily
备份之外的所有内容
- 进行备份,放入
- 每周:
- 进行备份,放入
weekly
目录 - 删除除最后 5 个
weekly
备份之外的所有内容
- 进行备份,放入
- 每个月:
- 进行备份,放入
monthly
目录 - 删除除最后 12 个
monthly
备份之外的所有内容
- 进行备份,放入
- 每年:
- 进行备份,放入
yearly
目录
- 进行备份,放入
您需要实现的逻辑数量大致相同,是吗?KISS。
这看起来更容易:
s3cmd ls s3://backup-bucket/daily/ | \
awk '$1 < "'$(date +%F -d '1 week ago')'" {print $4;}' | \
xargs --no-run-if-empty s3cmd del
或者,按文件数量而不是年龄:
s3cmd ls s3://backup-bucket/daily/ | \
awk '$1 != "DIR"' | \
sort -r | \
awk 'NR > 7 {print $4;}' | \
xargs --no-run-if-empty s3cmd del
答案2
例如,如果您只想保留 8 个每日备份和 5 个每周(每周日)备份,则其工作方式如下:
for i in {0..7}; do ((keep[$(date +%Y%m%d -d "-$i day")]++)); done
for i in {0..4}; do ((keep[$(date +%Y%m%d -d "sunday-$((i+1)) week")]++)); done
echo ${!keep[@]}
从今天(2014-11-10)起,这将输出:
20141012 20141019 20141026 20141102 20141103 20141104
20141105 20141106 20141107 20141108 20141109 20141110
作为留给您的练习,您只需删除所有名称未出现在keep
数组中的备份文件。
如果您还想保留 13 个月的每月备份(每月第一个星期日)和 6 年的备份(每年第一个星期日),事情就会变得稍微复杂一些:
for i in {0..7}; do ((keep[$(date +%Y%m%d -d "-$i day")]++)); done
for i in {0..4}; do ((keep[$(date +%Y%m%d -d "sunday-$((i+1)) week")]++)); done
for i in {0..12}; do
DW=$(($(date +%-W)-$(date -d $(date -d "$(date +%Y-%m-15) -$i month" +%Y-%m-01) +%-W)))
for (( AY=$(date -d "$(date +%Y-%m-15) -$i month" +%Y); AY < $(date +%Y); AY++ )); do
((DW+=$(date -d $AY-12-31 +%W)))
done
((keep[$(date +%Y%m%d -d "sunday-$DW weeks")]++))
done
for i in {0..5}; do
DW=$(date +%-W)
for (( AY=$(($(date +%Y)-i)); AY < $(date +%Y); AY++ )); do
((DW+=$(date -d $AY-12-31 +%W)))
done
((keep[$(date +%Y%m%d -d "sunday-$DW weeks")]++))
done
echo ${!keep[@]}
从今天(2014-11-10)起,这将输出:
20090104 20100103 20110102 20120101 20130106 20131103
20131201 20140105 20140202 20140302 20140406 20140504
20140601 20140706 20140803 20140907 20141005 20141012
20141019 20141026 20141102 20141103 20141104 20141105
20141106 20141107 20141108 20141109 20141110
与上述相同,只需删除此数组中未找到的所有备份文件。
答案3
我最近也遇到了同样的问题。恕我直言,尝试编写一个 shell 脚本来执行此操作非常痛苦,而使用具有内置函数(如集合、字典等)的高级语言编写一些可重用的逻辑则容易得多。一般的想法是采用配置来指示要保留每个时期的多少个文件,然后针对每个文件决定是否应保留。
有一个相当流行的基于 Python 的脚本,它看起来非常漂亮,并且有一些易于理解的源代码。此外,基于 Python 而不是基于 Shell 使其具有跨平台优势: https://github.com/xolox/python-rotate-backups
答案4
我也遇到过同样的问题。基本上需要文件系统上有空间来运行每日完整备份。
我编写了一个仅打印 rm -f 命令的脚本。
基础但对我来说已经足够好了。易于适应/改进。
好处:
- 超级简单(基于 find)
- 易于导入文件或仅复制/粘贴相关输出部分以供交互使用
- 可能会审查、执行并可能保留删除(清理)操作的痕迹
限制:
- 如果你的文件格式不同,你需要调整正则表达式
- 因为它只是基于文件计数,所以当月备份未运行或未启动时,可能会出现一个月的变化。
- 需要进行一些重写,因为它会在第二次运行时删除每周备份。因此只能使用一次。如果每月仅运行一次,并且在同一天,则可能会有效。
#!/bin/bash
# Build for this file format:
# ./YYYYmmdd-HHhMM_container_name.tar.xz
# example: ./20210923-06h00_petrussewp.tar.xz
DELETE_DAILY_AFTER=30 # days
DELETE_WEEKLY_AFTER=180 # days (so keep only one monthly)
# List the backup sources to group them:
sourcename=$( ls -1 20*.tar.?z | sed -E 's/^[-h0-9]+_(.+)\.tar\.[gbx]z$/\1/' | sort | uniq )
for ctname in ${sourcename}
do
echo "## ${ctname} backups :"
for month in {01..12}
do
# Delete daily backups older than DELETE_DAILY_AFTER days.
echo "# Obsolete daily backups for ${ctname} for the month ${month}:"
find ./ -maxdepth 1 -type f -name "20??${month}*${ctname}*" -mtime +${DELETE_DAILY_AFTER} -printf "rm -f %p\n" | sort -n | awk 'NR % 7 != 1'
# Delete all but the first matching backup of the month after DELETE_WEEKLY_AFTER days.
echo "# Obsolete weekly backups for ${ctname} for the month ${month}:"
find ./ -maxdepth 1 -type f -name "20??${month}*${ctname}*" -mtime +${DELETE_WEEKLY_AFTER} -printf "rm -f %p\n" | sort -n | awk 'NR % 7 == 1' | tail -n+2
done
done
脚本输出示例:
## wekan-db backups :
# Obsolete daily backups for wekan-db for the month 01:
# Obsolete weekly backups for wekan-db for the month 01:
# Obsolete daily backups for wekan-db for the month 02:
rm -f ./20210206-06h00_wekan-db.tar.xz
rm -f ./20210207-06h00_wekan-db.tar.xz
rm -f ./20210208-06h00_wekan-db.tar.xz
rm -f ./20210209-06h00_wekan-db.tar.xz
rm -f ./20210210-06h00_wekan-db.tar.xz
rm -f ./20210211-06h00_wekan-db.tar.xz
rm -f ./20210213-06h00_wekan-db.tar.xz
rm -f ./20210214-06h00_wekan-db.tar.xz
rm -f ./20210215-06h00_wekan-db.tar.xz
rm -f ./20210216-06h00_wekan-db.tar.xz
rm -f ./20210217-06h00_wekan-db.tar.xz
rm -f ./20210218-06h00_wekan-db.tar.xz
rm -f ./20210220-06h00_wekan-db.tar.xz
rm -f ./20210221-06h00_wekan-db.tar.xz
rm -f ./20210222-06h00_wekan-db.tar.xz
rm -f ./20210223-06h00_wekan-db.tar.xz
rm -f ./20210224-06h00_wekan-db.tar.xz
rm -f ./20210225-06h00_wekan-db.tar.xz
rm -f ./20210227-06h00_wekan-db.tar.xz
rm -f ./20210228-06h00_wekan-db.tar.xz
# Obsolete weekly backups for wekan-db for the month 02:
rm -f ./20210212-06h00_wekan-db.tar.xz
rm -f ./20210219-06h00_wekan-db.tar.xz
rm -f ./20210226-06h00_wekan-db.tar.xz