Shell 定时任务失败主因是 cron 环境与交互式 Shell 差异,需模拟其极简 PATH、工作目录和 环境变量 来排查,重点检查日志、路径、权限及重定向。

Shell 定时任务(cron job)执行失败,往往不是脚本本身有问题,而是执行环境和上下文差异导致的——cron 运行时没有交互式 Shell 的 环境变量、路径、工作目录等。排查核心思路是:模拟 cron 环境,逐层比对差异。
确认 cron 是否真正触发了任务
先排除“根本没跑”的情况:
- 检查系统级 cron 日志:sudo grep CRON /var/log/syslog(ubuntu/debian)或 sudo grep cron /var/log/messages(centos/RHEL),看对应时间点是否有调度记录
- 在 crontab 中加简单日志,例如:* * * * * date >> /tmp/cron-test.log 2>&1,确认基础调度通路正常
- 注意用户级 crontab 和系统级(/etc/crontab)语法不同:前者无用户名字段,后者有;写错格式会导致静默忽略
用 cron 真实环境复现命令
cron 默认使用/bin/sh,且 PATH 极简(通常只有/usr/bin:/bin)。直接在终端执行成功 ≠ cron 能执行成功:
- 手动模拟:运行 env -i PATH=/usr/bin:/bin /bin/sh -c ‘your-command’,观察是否报错
- 在 crontab 中显式指定完整路径:比如用 /usr/bin/python3 而非 python3,用/home/user/script.sh 而非./script.sh
- 脚本开头加上 #!/usr/bin/env bash 并确保有可执行权限(chmod +x),但更稳妥的是在 crontab 里明确调用/bin/bash /path/to/script.sh
检查脚本内部依赖的上下文
常见“本地能跑,cron 崩”的原因:
- 工作目录不确定 :cron 启动时 PWD 通常是用户家目录,脚本中用相对路径(如./data/input.txt)会找不到文件。 解决方法:脚本开头加cd “$(dirname “$0″)” 或用绝对路径
- 环境变量缺失:比如java_HOME、PYTHONPATH、自定义 PATH 等。可在 crontab 中提前导出:PATH=/usr/local/bin:/usr/bin:/bin JAVA_HOME=/usr/lib/jvm/default-java
- 需要 TTY 或交互输入 :含sudo、ssh 免密未配置、gpg解密等操作,在非交互环境下会卡住或失败。避免在 cron 中使用需交互的命令
让错误“看得见”——日志和重定向必须做
cron 默认只邮件通知标准错误(且常被忽略),务必主动捕获输出:
- 每条 crontab 命令末尾加上 >> /path/to/log.log 2>&1,例如:0 2 * * * /home/user/backup.sh >> /home/user/backup.log 2>&1
- 脚本内也建议加日志:开头写set -x(打印执行命令),关键步骤用echo “$(date): doing X” >> $LOG
- 测试阶段可临时把命令改成 sleep 10 && your-command,然后用ps aux | grep your-command 观察进程是否存在,判断是卡住还是秒退