定时任务的五个坑:为什么你的 cron 不按点跑
定时任务的五个坑:为什么你的 cron 不按点跑
坑一:环境变量不是你想的那样
手动执行脚本时它能跑,写成定时任务就报“找不到命令”,这是最常见的情况。
原因很简单:cron 执行时用的是最小环境,PATH 通常只有 /usr/bin:/bin,你在 .bashrc 里加的路径它一概不认。
解决办法有两个:要么在脚本里写全绝对路径,要么在脚本开头自己设好环境:
#!/bin/bash
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
我更推荐第一种——脚本里不依赖任何环境,走到哪都能跑。
坑二:% 在 crontab 里是换行符
这一条坑过无数人。crontab 里 % 有特殊含义,表示换行,后面的内容会被当成标准输入。
所以这行是坏的:
0 3 * * * /usr/bin/date +%F > /tmp/d.log
应该写成:
0 3 * * * /usr/bin/date +\%F > /tmp/d.log
或者干脆把逻辑放进脚本文件,crontab 里只调脚本。后者更省心。
坑三:没有输出,就以为成功了
cron 默认会把输出发邮件,但机器上通常没装邮件服务,或者邮件进了垃圾箱。结果就是:任务失败了,你什么都不知道。
稳妥的做法是每次执行都落日志,并且区分成功失败:
{
echo "start $(date)"
/path/to/task.sh
echo "exit=$? $(date)"
} >> /var/log/mytask.log 2>&1
日志要有轮转,不然迟早把磁盘写满。
坑四:上一轮还没跑完,下一轮又来了
如果一个任务执行时间可能超过调度间隔,就会出现重叠。两个进程同时改同一个文件,轻则数据错乱,重则互相删除。
最简单的防护是加锁:
exec 9>/var/lock/mytask.lock
flock -n 9 || exit 0
-n 表示拿不到锁就立刻退出,不排队。这样同一时刻只有一个实例在跑。
坑五:机器重启后任务补跑
有些实现(比如 anacron)会在开机后补跑错过的任务,但普通 cron 不会——关机期间该跑的任务就直接跳过了。
如果你的任务是“每天必须做一次”,就得自己补:在脚本里记录上次成功时间,启动时检查是否已经超期。
LAST=$(cat /var/lib/mytask.last 2>/dev/null || echo 0)
NOW=$(date +%s)
if [ $((NOW - LAST)) -gt 86400 ]; then
run_task
fi
一个习惯建议
调试定时任务时,别等真的到点。把时间设成下一分钟,看它到底跑不跑、报什么错,比盯着日志猜快得多。
另外,写完 crontab 记得用 crontab -l 确认真的写进去了——编辑器的保存失败是很常见的。
- 点赞
- 收藏
- 关注作者
评论(0)