cron任务重复执行的根本原因是任务执行时间超过调度间隔,导致新进程在旧进程未结束时被再次触发;可用flock加锁(如/5 * flock -n /tmp/myjob.lock -c "/path/to/script.sh")或PID文件+pgrep双重防护,并配合日志、超时和健康检查确保可靠性。

为什么 cron 任务会重复执行?
根本原因不是 cron 本身重复调度,而是任务执行时间超过 cron 间隔,导致新进程在旧进程尚未结束时被再次拉起。比如 */5 * * * * /path/to/script.sh
用 flock 防止并发执行最稳妥
flock 是 Linux 自带的文件锁工具,轻量、可靠、无需额外依赖。它通过持有文件描述符锁来确保同一时刻只有一个实例在运行。
实操写法:
*/5 * * * * flock -n /tmp/myjob.lock -c "/path/to/script.sh"
关键参数说明:
-
-n:非阻塞模式,如果锁已被占用,直接退出不等待 -
/tmp/myjob.lock:锁文件路径,建议用/tmp/下固定名称,避免因路径不存在导致锁失效 -
-c:后面跟要执行的命令(必须用双引号包裹含空格或重定向的命令)
注意:flock 锁在命令退出后自动释放,即使脚本崩溃也能释放(依赖内核行为,99% 场景可靠)。
用 pgrep + PID 文件做二次防护(适合复杂场景)
当脚本需要传参、重定向、或 flock 因挂载选项(如 noexec)不可用时,可用 PID 文件配合 pgrep 判断。
示例 cron 条目:
*/5 * * * * /path/to/script.sh --lockfile /tmp/myjob.pid
脚本内部需包含:
- 启动时检查
/tmp/myjob.pid是否存在且对应进程仍在运行(kill -0 $(cat /tmp/myjob.pid) 2>/dev/null) - 确认无活跃实例后,写入当前
$$到该文件 - 退出前
rm -f /tmp/myjob.pid(或用 trap 确保异常退出也清理)
风险点:PID 文件不会自动过期,若脚本被 kill -9 或机器宕机,残留 PID 文件会导致后续任务永久跳过——必须搭配超时判断(比如检查 PID 对应进程启动时间是否早于 30 分钟)。
别忽略 crond 的日志与调试细节
很多“重复执行”其实是误判:cron 日志没开、脚本静默失败、stdout/stderr 被丢弃,让人以为上一次没跑完,其实早就挂了。
- 在 cron 行末加
&>> /var/log/myjob.log记录完整输出 - 确认系统级 cron 日志开启:
/etc/rsyslog.d/50-default.conf中有cron.* /var/log/cron.log,然后重启rsyslog - 测试时用
run-parts --test /etc/cron.d/检查语法,用sudo -u $USER /bin/sh -x /path/to/script.sh模拟 cron 环境执行(注意 cron 默认 PATH 很窄)
真正难处理的是长周期任务 + 不稳定网络/IO 导致的偶发卡死,这时候单靠锁不够,得加超时控制(timeout 300s /path/to/script.sh)和健康检查。










