crontab 里的 % 会被当成换行:一个静默失败的命令

举报
yd_242458115 发表于 2026/11/07 10:53:13 2026/11/07
【摘要】 终端里手敲正常的命令,写进 crontab 就默默失败且不留错误。根因是 crontab 里 % 等价于换行,date +%Y%m%d 这类写法会让命令被截断。顺带讲同源的 PATH / LANG / TZ 缺失问题、flock 防并发重叠,以及让 crontab 只调脚本这条根治办法。

这个坑的特征是:命令在终端里手敲一切正常,写进 crontab 就默默失败,日志里连个错都没有。原因往往是 %。

一、% 在 crontab 里是换行符

crontab 的命令部分,% 有特殊含义:它等价于回车换行,后面第一个 % 之后的内容会被当成 stdin 喂给命令。

所以这条:

0 3 * * * tar -czf /backup/db-$(date +%Y%m%d).tar.gz /data

实际执行的结果是:tar -czf /backup/db-$(date + 被当成命令,Y%m%d)... 被当成标准输入。命令直接语法错误,退出码非零,但因为没重定向,错误只发到 crontab 的邮件里,而多数系统没配邮件,于是"什么都没发生"。

正确写法是把 % 转义:

0 3 * * * tar -czf /backup/db-$(date +\%Y\%m\%d).tar.gz /data

注意是 \%,不是 %%。

二、比转义更省事的办法:把逻辑挪进脚本

我的做法是把整个备份流程写成一个脚本文件,crontab 里只调脚本:

0 3 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1

脚本里想怎么写 % 都行,还能加错误处理、加锁、加通知。crontab 越短,踩特殊字符坑的概率越低。

三、同源的第二个坑:PATH 不一样

crontab 执行时用的是 bash 的非交互非登录模式,不读 /etc/profile、不读 ~/.bashrc。所以你在终端里能用的 node、ffmpeg、docker,在 cron 里可能直接 command not found。

两种解法:

# 法一:在 crontab 顶部显式声明
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

# 法二:脚本里用绝对路径
/usr/local/bin/node /opt/scripts/task.js

我倾向法二,因为法一在每个用户、每台机器上都得重配一遍,容易漏。

四、还没结束:环境变量也是干净的

LANG=zh_CN.UTF-8

五、并发重叠:上一次还没跑完,这一次又来了

cron 不会管上一次是否结束。如果任务耗时超过间隔,就会出现多个实例同时跑,互相踩文件。用 flock 加个锁:

*/5 * * * * flock -n /var/lock/mytask.lock /opt/scripts/task.sh

六、验证方法

写进 crontab 之后别急着等。先把时间设成下一分钟,然后盯日志:

# 临时改成每分钟,验证完再改回来
* * * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1

另一个技巧:在脚本开头加一行调试输出,把关键环境打印出来。第一次跑完看一眼就知道环境对不对:

#!/bin/bash
echo "=== $(date) PATH=$PATH LANG=$LANG TZ=$TZ PWD=$(pwd)"

速查表

症状大概率原因修法
完全不执行,无报错命令里有 %转义成 \% 或挪进脚本
command not foundPATH 不含该命令用绝对路径
中文乱码 / 编码异常没有 LANGcrontab 里声明 LANG
时间戳差 8 小时没有 TZ声明 TZ=Asia/Shanghai
同一任务跑了多份没有互斥flock -n
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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