必须确认脚本可独立执行,因crontab默认环境极简(PATH仅/usr/bin:/bin、shell为/bin/sh),易因路径、环境变量、权限、时区等问题导致失败,务必用绝对路径、显式声明解释器、重定向输出并模拟环境测试。

crontab -e 编辑定时任务前必须确认脚本可独立执行
直接往 crontab 里加一行就跑,结果脚本不执行或报错,八成是环境问题。cron 默认使用 /bin/sh,且 $PATH 极其精简(通常只有 /usr/bin:/bin),所以脚本里用的命令如 find、date 虽然存在,但自定义路径下的工具(比如 /opt/backup/clean.sh 里调了 python3)很可能找不到。
实操建议:
- 脚本开头显式声明解释器,比如
#!/bin/bash,并在crontab中指定 shell:SHELL=/bin/bash - 所有命令尽量用绝对路径:
/usr/bin/find、/bin/rm,避免依赖$PATH - 在终端下模拟 cron 环境测试:先清空环境变量再运行脚本
env -i /bin/bash --noprofile --norc /path/to/clean.sh - 务必重定向输出,否则失败无声:
/path/to/clean.sh >> /var/log/backup_clean.log 2>&1
清理逻辑别只靠文件名里的“日期”,要用 find -mtime 或 -mmin
很多人把备份文件按 backup_20240501.tar.gz 命名,然后在脚本里用 date 和字符串截取判断是否过期——这非常脆弱:文件可能被误 touch、命名格式不统一、时区混乱,导致该删没删或误删。
更可靠的方式是依据文件最后修改时间(即实际备份完成时间)来清理:
-
/usr/bin/find /backup -name "*.tar.gz" -mtime +7 -delete:删 7 天前修改的文件(注意:-mtime +7 表示“大于 7×24 小时”,即第 8 天及以后) - 若需精确到小时,用
-mmin +4320(7 天 = 4320 分钟) - 先测试再删除:把
-delete换成-ls或-print,确认列出的是预期文件 - 避免误删:加
-maxdepth 1限制只扫当前目录,不递归子目录
crontab 时间表达式写错会导致“看似生效实则从不触发”
常见错误是把“每天凌晨 2 点”写成 0 2 * * *,却忘了服务器时区不是本地时区;或者用了 */2 却没意识到它从 0 分开始计(即 0、2、4… 分触发),而你希望的是整点触发。
关键检查点:
- 确认系统时区:
timedatectl status | grep "Time zone",必要时sudo timedatectl set-timezone Asia/Shanghai - 分钟字段在前,顺序是:
分 时 日 月 周,别和某些 Web UI 的“秒 分 时…”混淆 - 周几和日不能同时设为具体值(如
0 2 1 * 1是无效的),cron 会忽略整行 - 特殊符号要转义:脚本路径含 % 需写成
\%,否则 cron 会把 % 后内容当 stdin
权限与用户上下文不匹配会让 rm 失败但无提示
如果备份由 root 写入(比如 rsync 用 sudo),而 crontab 是普通用户(如 backupuser)添加的,那 rm 会因权限不足静默失败——尤其当你没加 2>&1 重定向时,根本看不到 Permission denied。
解决方式取决于你的部署模型:
- 统一用
root执行:用sudo crontab -e编辑 root 的 crontab,并确保脚本中所有路径和命令对 root 可读可删 - 改用 backupuser 管理全部流程:备份也由该用户发起,确保文件属主一致;或用
chown backupuser:backupuser /backup/*定期修正 - 不要在脚本里加
sudo rm—— cron 不会给你输密码,且暴露安全风险 - 加一句校验:
if [ ! -w "/backup" ]; then echo "$(date) /backup not writable" >&2; exit 1; fi
真正容易被忽略的是日志权限和磁盘满风险:/var/log/backup_clean.log 如果长期不轮转,本身就会撑爆根分区;而 cron 日志默认不记录 stdout/stderr,全靠你手动重定向。一旦脚本某天因磁盘满无法写日志,又没配 2>&1,问题就彻底隐身了。











