crontab 不执行怎么办:8 步排查清单
这是运维和开发里最常见的困惑之一:脚本手动运行一切正常,加进 crontab 之后却“没反应”。其实 cron 并不是坏了,而是它运行任务的环境和你登录后的终端有很多不同。下面按从易到难的顺序给出 8 步排查清单,每一步都能直接动手验证。时间表达式本身怎么写,见 《Cron 表达式怎么写》。
第 1 步:确认 cron 服务在运行
先排除最基础的原因。Debian、Ubuntu 上服务名一般是 cron,红帽系发行版常见的是 crond,具体以你所用发行版的文档为准:
systemctl status cron
systemctl status crond
如果显示未运行,用 systemctl start 启动并设置开机自启。容器环境里往往根本没有安装或没有启动 cron,需要另行处理。
第 2 步:看日志,确认任务有没有被触发
有没有“被触发”和“有没有执行成功”是两件事,先分清楚。Debian 系可以这样查:
grep CRON /var/log/syslog
journalctl -u cron --since today
红帽系一般看 /var/log/cron。如果日志里能看到你的任务那一行,说明 cron 已经调起了命令,问题在命令本身;如果完全没有,问题在 crontab 的写法或服务。
第 3 步:检查 crontab 本身的写法
- 字段数量:用户 crontab(
crontab -e)是 5 个时间字段加命令;而系统 crontab(/etc/crontab和/etc/cron.d/下的文件)在时间字段后面还要多写一个用户名。把用户 crontab 的写法放进/etc/cron.d/会因缺少用户名而不生效。 - 末尾换行:crontab 文件的最后一行需要以换行结束,否则最后一条任务可能被忽略。编辑完按回车再保存。
- 时间字段:把表达式贴进 Cron 表达式解析器,确认“下次运行时间”符合预期。
第 4 步:核对时区和系统时间
cron 按服务器的系统时区计时。服务器若设为 UTC,你写的“每天 9 点”会在北京时间下午 5 点执行。用 date 和 timedatectl 看一眼。需要核对具体时刻时,可以用 时间戳转换工具 把日志里的时间戳转成北京时间。
第 5 步:使用绝对路径
cron 的环境变量非常精简,尤其是 PATH,远不如交互式终端里完整。因此:
- 命令和脚本都写绝对路径,如
/usr/bin/python3 /opt/job/run.py,用which python3查路径。 - 脚本里读写文件也用绝对路径,别依赖当前目录。
- 需要某些环境变量,在脚本开头显式设置,或在 crontab 顶部写
PATH=...(注意不会做变量替换)。
第 6 步:百分号 % 要转义
在 crontab 的命令里,未转义的 % 会被当作换行,后面的内容会变成标准输入。最典型的例子是用日期命名文件:
# 错误
0 2 * * * tar czf /bak/a-$(date +%F).tgz /data
# 正确:% 前加反斜杠
0 2 * * * tar czf /bak/a-$(date +\%F).tgz /data
更省事的办法是把复杂命令写进脚本文件,再让 cron 调用脚本。具体规则以 man 5 crontab 为准。
第 7 步:权限和文件名
- 脚本需要有可执行权限,
chmod +x,或者直接用解释器调用。 - 运行用户对目标目录必须有读写权限,root 的 crontab 和普通用户的 crontab 是两份。
- 放进
/etc/cron.daily、/etc/cron.hourly这类目录的脚本,在 Debian 系上由run-parts执行,文件名只允许字母、数字、下划线和连字符。带点的名字,如backup.sh,会被静默忽略,去掉后缀即可。
第 8 步:把输出留下来
最后一招,也是最有效的习惯:让任务把标准输出和错误都写进日志,别丢弃:
*/10 * * * * /opt/job/run.sh >> /var/log/job.log 2>&1
下次失败时,日志里的报错信息往往直接指出原因,比如“command not found”(路径问题)或“Permission denied”(权限问题)。为了防止日志无限增长,记得配合日志轮转。
一个完整的例子
假设你想每天凌晨 2 点备份一个目录,写了这样一行,却始终没有备份文件:
0 2 * * * tar czf /bak/d-$(date +%F).tgz /data
按清单走:第 1 步服务正常;第 2 步日志里能看到任务被调起,说明时间字段没问题;第 6 步发现 %F 没有转义,命令被从百分号处截断。改成 \%F,并在末尾加上 >> /var/log/bak.log 2>&1,第二天检查日志和备份文件即可确认。这类问题没有任何报错弹出来,只有看日志才能发现。
实用小技巧
- 先把时间改成“每分钟”,确认任务能被调起,再改回真正的时间。
- 在脚本开头加一行
date >> /tmp/job.start,用来证明脚本有没有开始运行。 - 相同任务可能并发重叠,长任务建议加上锁(如
flock),避免上一次没跑完下一次又启动。 - 如果输出内容里有特殊字符,需要匹配或提取时,可用 正则测试工具 先调试规则。
还有一点值得提醒:修改 crontab 之后不需要重启服务,cron 会自动读取变化;但如果你改的是别人的账号下的任务,要用 crontab -u 用户名 -l 查看,不要在错误的账号里反复修改。每次调整后,最好记录修改时间和原因,方便以后追溯。
按上面 8 步走一遍,绝大多数“cron 不执行”的问题都能定位。不同发行版的路径和服务名略有差异,请以官方文档为准,参考 Debian 的 crontab(5) 手册。