以为玩透了crontab!直到定时任务偷偷罢工了一整周
我一直觉得自己挺懂定时任务的。
不就是五个星号加一条命令嘛。分时日月周,写好时间,写好脚本,crontab -e 一粘,完事。能有多难?
直到那一周。
事情的起因
那个周一,我心血来潮检查了一下备份服务器,发现最近七天的备份日志……是空的。
不是报错,不是失败,是根本没有日志文件产生。
我心里咯噔一下,第一时间怀疑是不是整个备份流程都挂了。赶紧去翻实际备份出来的文件——还好,数据都在,每一天的备份包都规规矩矩躺在那里。
也就是说:备份成功了,但日志没生成。
这个诡异程度,大概就是"你打了人,人没叫"那种感觉。
一通乱查
我自信满满地开始排查。
备份脚本是这么个结构:先打包,再 echo 一行成功信息重定向到日志文件,日志文件名用日期拼出来:
手动跑一遍,日志生成得好好的。
那为什么 cron 跑就没日志?
我开始疯狂怀疑一切可能:
- 是不是 cron 服务挂了?
systemctl status crond,在跑,没问题。
- 是不是权限问题?cron 是 root 跑的,
/var/log写得动,没问题。
- 是不是
$()在 cron 里不支持?换成反引号,还是没日志。
- 是不是 PATH 问题导致
date命令找不到?我特意在脚本里用/bin/date全路径调用,还是没日志。
- 是不是 SELinux 拦了?查了一圈 audit 日志,没相关记录。
- 是不是脚本根本没被 cron 执行?我在脚本开头加了个
touch /tmp/cron-ran,结果这个文件出现了。说明脚本确实跑了,备份也确实做了,就偏偏那条echo死活不产日志。
一整个下午,我在本机和备份服务器之间来回切换,脚本改了又改,重定向符号试了 > >> 2>&1 各种排列组合。没有。
五点半,我快要把锅甩给"这台机器中邪了"的时候,我做了一件事:我把 crontab 里那行原封不动复制出来,贴到终端里手动跑了一遍。
手动跑,日志生成。
盯着终端里那行命令看了大概十秒,我突然注意到一个细节——终端里一切正常,但 crontab 里有那么一个字符,在我的记忆里一直觉得它"无害"。
那个字符是:%。
真相
熟悉 cron 的人这时候大概要笑出声了。
crontab 里,% 是一个有特殊含义的字符——它被当作换行符。这是 cron 的一条古老规则:命令部分里,第一个 % 之后的内容会被当作标准输入喂给命令,% 本身被替换成换行。
于是我的 $(date +%Y%m%d),在 cron 眼里变成了这样:
(换行)
(换行)
date 拿到的参数变成了一坨跨好几行的乱码,要么报错,要么吐出一串谁都看不懂的东西。而那条 echo 的重定向,因为命令本身已经被换行符拆得稀碎,整个命令结构都变了——日志文件名根本没拼出来,重定向落空,那行 echo 静默地、优雅地、什么也没干。
备份的主体逻辑不依赖 %,所以照常成功;只有那条带 date +%... 的 echo,在 cron 里被无声地肢解了。
解决办法简单到令人窒息:在 % 前面加反斜杠转义,写成 \%。或者把日期拼接抽到脚本内部去执行,crontab 只负责调用脚本——脚本内部是不受 % 规则影响的。
我把日期拼接挪进了脚本里,保存,收工。从那以后日志再没缺过。
几条血泪教训
折腾一周换来的几句话,送给每一个"觉得自己懂 crontab"的人:
- crontab 里的
%不是普通字符,它是换行符。 这一条坑过的人能从机房排到大门口。凡是 crontab 命令行里直接写date +%Y%m%d这种,百分号必须转义为\%,否则就是一场静默的灾难。
- "手动能跑,cron 不行"几乎等于"环境差异问题"。 cron 的执行环境和你登录的 shell 完全是两个世界:PATH 极简、环境变量几乎没有、工作目录是 HOME。排查 cron 问题,第一步永远是"假设它在一个空荡荡的 shell 里跑",而不是"我手动跑过了所以没问题"。
- 静默失败是最阴险的失败。 备份能成、日志没成,这种"部分成功"比"整体崩溃"可怕得多——因为你会很长一段时间都以为一切正常。给关键任务加一个"执行后必须留下可见痕迹"的校验,比加一万行日志都有用。
- 能放进脚本里的逻辑,就别留在 crontab 里。 crontab 只负责"什么时候、调谁",具体命令越短越好。日期拼接、条件判断、复杂重定向,统统塞进脚本文件。crontab 里的命令越像"一句话",越不容易踩到 cron 那一堆藏在语法里的特殊规则。
最后
那一周之后,我对 crontab 的态度从"五个星号而已"变成了"五个星号,外加一堆埋在语法里的地雷"。
越是看起来人畜无害的工具,翻车时越没有现成文档可查——因为大家都觉得它"简单",没人专门写"我因为一个百分号排查了一周"这种事。
所以我写了。
希望你看完之后,下次往 crontab 里粘带 date 的命令,第一反应是"转义百分号了吗",而不是像我一样,从 SELinux 开始怀疑。那个 %,真的很重要。
- 点赞
- 收藏
- 关注作者
评论(0)