定时任务为什么没跑:Linux cron 的环境与日志排查
先说结论
cron 任务“没跑”,多数时候并不是 cron 没触发,而是任务在一个和你交互式终端不一样的环境里失败了,而且错误没人看到。排查的思路是:先确认有没有触发,再确认环境差异,最后让输出可见。
一、cron 的环境和终端不同
cron 启动任务时环境很精简:PATH 通常只有 /usr/bin:/bin 一类的少数目录(具体取决于发行版和 cron 实现),不会像登录 shell 那样加载你的配置文件。默认 shell 一般是 /bin/sh,不是 bash;工作目录通常是用户主目录,而不是脚本所在目录。
所以建议:
- 命令和脚本一律写绝对路径,可用 command -v 查到命令位置。
- 脚本开头显式 cd 到所需目录,或在脚本里用绝对路径读写文件。
- 需要的环境变量在脚本内显式设置,或在 crontab 顶部声明 PATH、SHELL。
二、crontab 五字段与时区
五个字段依次是:分、时、日、月、星期,之后才是命令。常见错误是把“日”和“星期”同时限定,二者在多数实现中是“或”的关系,可能比预期触发得更频繁。另外,命令里的百分号在 crontab 中有特殊含义,需要用反斜杠转义,例如 date +%F 要写成 date +%F。
时区务必核实,不要想当然认为是北京时间。cron 通常按守护进程所在机器的本地时区计时,服务器可能是 UTC。用 date 和 timedatectl 查看当前时区,再换算;机器本地时区不等于北京时间,除非你确认它设置为 Asia/Shanghai。
三、让输出可见:stdout 与 stderr
默认情况下输出会尝试通过本地邮件发送,很多机器没配置邮件,错误就丢了。建议显式重定向:
*/5 * * * * /usr/local/bin/job.sh >> /var/log/job.log 2>&1
注意 2>&1 要写在重定向文件之后。确保日志文件所在目录对该用户可写。
四、查看 cron 自身日志
cron 是否触发,要看系统日志,位置因发行版而异:
- 使用 systemd 的系统上,可以尝试 journalctl -u cron 或 journalctl -u crond,服务名不同发行版不同,可先用 systemctl 查一下。
- 部分 Debian 系统可能写到 /var/log/syslog,部分 RHEL 系系统是 /var/log/cron。
- 个别精简系统可能没有持久化日志,需要自行确认。
日志里能看到“触发了命令”,但看不到命令是否成功,成功与否仍要看你自己的日志。
五、用 flock 避免重叠执行
任务耗时超过间隔时,会出现多个实例并发。可以用 flock 加锁:
*/5 * * * * /usr/bin/flock -n /tmp/job.lock /usr/local/bin/job.sh >> /var/log/job.log 2>&1
-n 表示拿不到锁就直接退出。但它不是万能的:只对同一台机器、同一把锁文件有效,多机部署无法互斥;锁文件放在不同位置也会失效;被跳过的那次不会补跑,需要业务自己能容忍。
六、最小验证与幂等性
先用一条最简单的任务验证 cron 本身:每分钟往日志里追加一行 date 的输出,确认能触发、时间对得上,再换成真实脚本。之后可以用 env -i 配合 /bin/sh 手动运行脚本,模拟精简环境,比等 cron 触发更快。
最后,任务应该设计成幂等:重复执行或补跑一次不会产生错误结果,例如写文件先写临时文件再改名,处理数据按唯一键去重。这样即使因重叠、重启或手动重跑多执行了一次,也不会出事。
排查清单
- 日志里是否有触发记录?
- 时区和五个字段对吗?
- 路径是否都是绝对路径?
- 输出是否重定向并检查了错误?
- 是否有重叠,是否需要 flock?
- 点赞
- 收藏
- 关注作者
评论(0)