crontab 里 PATH 没配对,脚本静默失败排查记
前两天写了个数据库备份脚本,手动执行一切正常,丢进 crontab 之后却迟迟没看到备份文件生成。看日志也没有任何报错,仿佛任务压根没跑。折腾了一圈才意识到,这是 cron 环境下 PATH 没配对导致的「静默失败」,分享一下排查思路,免得你再踩。
现象
脚本逻辑很简单:用 venv 里的 python 跑一个导出脚本,再把结果 rsync 到备份机。手动 ./backup.sh 跑,几秒钟出文件;加到 crontab:
0 3 * * * /opt/scripts/backup.sh
第二天一看,备份目录空空如也。把脚本改成把输出写日志:
0 3 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
再看日志,里面赫然一行:python: command not found,紧接着 rsync 也找不到。
根因
cron 启动任务时,不会加载你的 .bashrc、.profile,也不会读取交互 shell 的环境。它的默认 PATH 通常只有 /usr/bin:/bin,有的发行版甚至更短。而我脚本里直接写的是 python(指望走 venv 或 /usr/local/bin/python),rsync 也依赖 PATH 找到。在这么短的 PATH 下,两个命令都找不到,脚本第一行就挂了,又因为没重定向,错误被 cron 发到 mail(往往没人看),于是「静默失败」。
还有一个更隐蔽的:即便命令能找到,cron 里一些环境变量(HOME、LANG、TZ)也和登录 shell 不同,依赖这些的脚本也会行为异常。
解决办法
第一,永远在 crontab 里显式声明 PATH。 在 crontab 文件顶部加:
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
SHELL=/bin/bash
这样至少覆盖了大部分命令位置。
第二,脚本里用绝对路径。 与其赌 PATH,不如直接写死:/opt/venv/bin/python、/usr/bin/rsync。最稳妥,不依赖任何外部环境。
第三,脚本开头 source 一下环境。 如果脚本必须在特定虚拟环境跑,开头显式:
source /opt/venv/bin/activate
或者 export PATH=/opt/venv/bin:$PATH。
第四,也是最重要的:cron 任务必须重定向日志。 无论成功失败,都把 stdout/stderr 落盘。否则一旦失败,你连哪里错都不知道。我现在的习惯是每条 crontab 都带 >> /var/log/xxx.log 2>&1,再配合 logrotate 防止日志膨胀。
小结
cron 任务的第一原则是:假设运行环境是一个「裸」环境,路径、变量、解释器全部显式声明,输出全部落日志。一旦遵守这条,绝大多数「定时任务不执行」的问题都会消失。剩下那些,再去查权限、锁文件、和被调度器本身。
- 点赞
- 收藏
- 关注作者
评论(0)