简介
一次实用的备份不只是执行归档命令。你需要了解归档包含哪些内容,确认它可以正常读取,能够恢复所需文件,并让无人值守的执行过程可被观察和追踪。
在本实验中,你将根据清单备份一个小型应用工作区,恢复指定的单个文件,将整个流程转换为带时间戳的脚本,并安装 Cron 计划。所有自动化路径都使用绝对路径,因此任务不会依赖交互式 Shell 的当前目录。
使用清单定义备份范围
在此步骤中,你将创建一个清单:它是一个纯文本路径列表,让备份范围清晰可见且便于审核。
进入工作区:
cd /home/labex/project/backup-lab
创建一个清单,其中包含相对于 data 目录的路径:
cat > backup-manifest.txt <<'EOF'
config/app.conf
documents/runbook.txt
EOF
为条目添加编号,便于查看:
nl -ba backup-manifest.txt
使用 while 循环确认清单中列出的每个源文件都存在。运行前,先了解其结构:
IFS=防止 Shell 删除行首或行尾的空白字符;read -r item将一整行读取到item中,同时不对反斜杠做特殊处理;do ... done包含针对每一行重复执行的命令;< backup-manifest.txt将清单作为循环的输入;test -f检查对应路径是否为普通文件,只有检查成功时,&&才会输出ready。
while IFS= read -r item; do
test -f "data/$item" && echo "ready: $item"
done < backup-manifest.txt
未列入清单的 documents/scratch.txt 文件特意不纳入此次受管理的备份范围。
根据清单创建归档
在此步骤中,你将使用 tar -T 从清单中读取文件名。-C data 选项会让归档使用简短的相对路径。
tar -czf backups/manual-backup.tar.gz -C data -T backup-manifest.txt
各选项含义如下:
-c:创建归档;-z:使用 gzip 压缩;-f:使用后面的归档文件名;-C data:以data作为相对路径的基准目录;-T backup-manifest.txt:从清单中读取要加入归档的文件。
检查生成的文件:
ls -lh backups/manual-backup.tar.gz
将其时间戳和大小与源文件进行比较:
stat -c '%n size=%s modified=%y' backups/manual-backup.tar.gz data/config/app.conf data/documents/runbook.txt
在不解压的情况下验证归档
在此步骤中,你将先测试归档,再正式依赖它。首先测试 gzip 数据流:
gzip -t backups/manual-backup.tar.gz
没有任何输出表示压缩数据流通过了完整性检查。现在列出 tar 中的文件,但不要解压:
tar -tzf backups/manual-backup.tar.gz | tee archive-contents.txt
将保存的文件列表与清单进行比较:
diff -u backup-manifest.txt archive-contents.txt
命令不应显示任何差异。确认未受管理的临时文件不在归档中:
if tar -tzf backups/manual-backup.tar.gz | grep -qx 'documents/scratch.txt'; then
echo "Unexpected scratch file found"
else
echo "Archive scope is correct"
fi
恢复指定的单个文件
在此步骤中,你将只恢复一个指定文件,避免覆盖无关数据。先模拟配置文件被意外删除:
rm data/config/app.conf
test -e data/config/app.conf || echo "app.conf is missing"
将 config/app.conf 单独解压到恢复目录:
rm -rf restore/*
tar -xzf backups/manual-backup.tar.gz -C restore config/app.conf
查看恢复的文件,然后将其复制回工作目录:
cat restore/config/app.conf
cp restore/config/app.conf data/config/app.conf
cmp -s restore/config/app.conf data/config/app.conf && echo "Configuration restored"
整个过程中只解压了归档中指定的成员文件。
创建带时间戳的备份脚本
在此步骤中,你将创建一个带时间戳的备份脚本。Cron 启动任务时使用的是受限环境,因此无人值守脚本内部应使用绝对路径。
创建 backup.sh:
cat > backup.sh <<'EOF'
#!/bin/bash
source_dir=/home/labex/project/backup-lab/data
manifest=/home/labex/project/backup-lab/backup-manifest.txt
backup_dir=/home/labex/project/backup-lab/backups
timestamp=$(date +%Y%m%d-%H%M%S)
archive="$backup_dir/backup-$timestamp.tar.gz"
mkdir -p "$backup_dir"
if [ ! -d "$source_dir" ] || [ ! -f "$manifest" ]; then
echo "Backup source or manifest is missing" >&2
exit 1
fi
/bin/tar -czf "$archive" -C "$source_dir" -T "$manifest" || exit 1
/bin/tar -tzf "$archive" > "$archive.contents" || exit 1
echo "Created $archive"
exit 0
EOF
为脚本添加可执行权限,并立即测试,而不是等待 Cron 执行:
chmod u+x backup.sh
./backup.sh | tee last-backup.txt
ls -1 backups
时间戳会为每次执行生成唯一的文件名。对应的 .contents 文件会准确记录归档中包含的内容。
安装并检查 Cron 计划
在此步骤中,你将安装并检查个人 Cron 计划。Crontab 条目由 5 个计划字段和后面的命令组成:
| 位置 | 字段 | 通常范围 |
|---|---|---|
| 1 | 分钟 | 0–59 |
| 2 | 小时 | 0–23 |
| 3 | 月中的第几天 | 1–31 |
| 4 | 月份 | 1–12 |
| 5 | 星期几 | 0–7,星期日为 0 或 7 |
星号 * 表示该字段允许的所有值。像 */5 这样的步长表示该字段每 5 个单位执行一次。
minute hour day-of-month month day-of-week command
例如,*/5 * * * * 表示每 5 分钟执行一次;0 2 * * * 表示每天凌晨 02:00 执行。
crontab -e 会在编辑器中打开个人 Crontab。一个可靠的流程是先准备并安装已知正确的条目,再进行一次小型交互式编辑。创建一个使用绝对路径并同时记录标准输出和标准错误的条目:
cat > backup.crontab <<'EOF'
*/5 * * * * /home/labex/project/backup-lab/backup.sh >> /home/labex/project/backup-lab/logs/backup.log 2>&1
EOF
查看并安装它:
cat backup.crontab
crontab backup.crontab
现在使用 Nano 打开已经安装的 Crontab:
EDITOR=nano crontab -e
在计划任务上一行添加以下注释:
## LabEx managed backup schedule
按 Ctrl+O,再按 Enter 保存。按 Ctrl+X 退出 Nano 并返回 shell。使用 crontab -l 确认 Cron 实际读取的内容,并保存该输出:
crontab -l | tee installed-crontab.txt
输出中应先出现注释,随后是每五分钟执行一次的条目。检查已安装的结果,可以在真正依赖该计划前发现编辑错误。
测试日志并理解百分号转义
在此步骤中,你将测试备份日志,并了解 Cron 对百分号的特殊处理。首先手动运行计划任务中的命令:
/home/labex/project/backup-lab/backup.sh >> /home/labex/project/backup-lab/logs/backup.log 2>&1
tail -n 5 logs/backup.log
find backups -maxdepth 1 -type f -name 'backup-*.tar.gz' -printf '%f\n' | sort
时间戳是在 backup.sh 内部生成的,因此普通的 % 字符在脚本中可以直接使用。不过,Cron 会对 Crontab 命令中的未转义 % 做特殊处理。如果直接在 Crontab 条目中使用 date,必须为每个百分号添加反斜杠进行转义。
保存一个正确转义的示例,供后续参考:
cat > cron-date-example.txt <<'EOF'
0 2 * * * /bin/echo "backup-$(/bin/date +\%Y\%m\%d-\%H\%M\%S)" >> /home/labex/project/backup-lab/logs/date-example.log 2>&1
EOF
cat cron-date-example.txt
当前安装的任务仍然使用更简单的脚本方式。它采用绝对路径,为每个归档添加时间戳,并留下可在无人值守执行后检查的日志。
总结
你使用清单定义了备份范围,借助 tar -T 创建了归档,在不解压的情况下完成了验证,并恢复了指定的单个文件。随后,你编写了使用绝对路径的带时间戳备份脚本,安装并检查了 Cron 计划,将无人值守执行的输出重定向到日志中,并了解了当 date 直接出现在 Crontab 命令中时为什么必须转义百分号。



