手动跑得好好的脚本,一进 cron 就失败:PATH、工作目录和 % 的三个坑

举报
yd_291384559 发表于 2026/11/03 06:09:27 2026/11/03
【摘要】 同一个脚本,终端里跑正常,进 crontab 就失败,八成是环境差异。本文按顺序讲三个坑:cron 不读 shell 配置导致 PATH 只剩 /usr/bin:/bin、工作目录是用户家目录导致相对路径错位、未转义的百分号会直接截断命令。附探针命令、flock 防重叠、CRON_TZ 时区写法,以及一条从退出码入手的排查顺序。

同一个脚本,登录终端执行一切正常,写进 crontab 之后就开始报错。绝大多数情况不是 cron 有 bug,而是它给出的执行环境和你的交互终端根本不是一回事。下面按三个坑的顺序给定位命令和修法。

一、cron 的 PATH 短到超乎想象

cron 启动任务时不会读 ~/.bashrc、~/.profile 或 /etc/profile,所以你在 shell 配置里 export 的变量一个都拿不到,PATH 通常只剩 /usr/bin:/bin。凡是装在 /usr/local/bin、/opt 下面,或者依赖 conda、nvm、go 环境的命令,都会以退出码 127 加一行 command not found 收场。

定位办法很直接,先塞一行探针把环境打到日志里,再跑真活:

* * * * * /bin/sh -c 'echo PATH=$PATH; echo HOME=$HOME; command -v python3; pwd' >> /tmp/cron-probe.log 2>&1

确认是 PATH 之后有两种修法。省事的是在 crontab 顶部显式赋值,它对同一文件里的所有任务生效:

PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
0 3 * * * /home/app/backup.sh

更稳的是脚本里所有外部命令一律写绝对路径,不依赖任何隐式查找。两个一起做最好。

还要注意 SHELL 本身。Debian 系上 cron 默认用 /bin/sh,而它是 dash,不支持双方括号、数组和部分 bash 扩展语法。脚本首行写了 shebang 也未必管用:如果 crontab 里是以 sh script.sh 的形式调起来,shebang 就被绕过了。给脚本加执行权限、crontab 里直接写路径本身,才会按 shebang 走。

二、工作目录不是你以为的那个

cron 执行任务时的工作目录是用户的家目录,不是脚本所在目录。于是脚本里所有相对路径都会指向错误位置,症状通常是文件找不到,或者产物莫名其妙出现在家目录里。修法是开头显式切目录,并且让失败可见:

cd "$(dirname "$0")" || exit 1

这一行顺带解决了从不同目录调用脚本、行为不一致的老问题。

三、百分号在 crontab 里是元字符

这是最隐蔽的一个。crontab 语法里未转义的百分号表示命令结束、后面接的是喂给进程的 stdin,所以 date +%Y%m%d 会被从百分号处截断,实际执行的命令只剩一个 date +。必须写成转义形式:

0 1 * * * /usr/local/bin/backup.sh > /var/log/backup-$(date +\%Y\%m\%d).log 2>&1

嫌转义难读的话,把日期计算挪进脚本、crontab 里只留一条最简调用,是更清爽的做法。

四、另外两件容易被忽略的事

其一是重叠执行:上一轮还没跑完,下一轮又起来了,两个进程互相踩数据。cron 自己不防重,用 flock 包一层就够了,拿不到锁直接退出:

/usr/bin/flock -n /var/lock/backup.lock /home/app/backup.sh

其二是时区。cron 按系统本地时区解释时间字段,容器里尤其容易和宿主机不一致。需要固定时区时,RHEL 系可以在 crontab 顶部写 CRON_TZ=Asia/Shanghai;或者改用 systemd timer,它按 UTC 存储、按本地时区显示,systemctl list-timers 能直接看到下一次触发的时间。

五、一条排查顺序

遇到 cron 失败,按这个顺序走基本不会漏:先把输出重定向到日志,确认它到底有没有被触发;再看退出码,127 是命令找不到、126 是不可执行、1 是脚本内部错误;然后在家目录和脚本目录两处分别找产物,判断是不是相对路径问题;最后把整条命令用 crontab 同样的 SHELL 和 PATH 手工复现一遍:

env -i PATH=/usr/bin:/bin HOME=/home/app /bin/sh -c '你的命令'

能在这条命令里复现出来的故障,就都是环境问题而不是代码问题。

小结:cron 只保证按时执行,不保证按你的环境执行。绝对路径、开头显式 cd、转义百分号这三件事做完,八成的定时任务故障会消失;剩下的两成交给 systemd timer 的日志和状态查询来收。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。