直接用 flock -xn 锁文件 -c '命令' 可彻底避免 crontab 任务重复执行:-x 加独占锁、-n 抢不到立即退出、-c 后单引号包裹完整命令;锁文件须避开 /tmp,推荐 /var/run/ 或项目路径;失败时加 || echo ... 记录日志便于排查。

直接在 crontab 条目里用 flock -xn 包裹命令,就能彻底避免“上一个没跑完、下一个又启动”导致的重复执行和资源冲突——它不排队、不等待、不残留,是目前最轻量也最可靠的解法。
核心写法:三要素缺一不可
必须同时满足以下三点,才能真正起效:
- -x(exclusive):加独占锁,确保同一时间最多一个实例运行
- -n(nonblock):抢不到锁立刻退出,返回码为 1,不等、不卡、不堆积
-
-c 后跟单引号包裹的完整命令:包括脚本路径、重定向、错误处理,例如:
'/path/to/script.sh >> /var/log/job.log 2>&1'
锁文件位置要稳,不能乱放
锁文件路径直接影响稳定性,选错等于白配:
- 别用
/tmp/xxx.lock:很多系统(如 Ubuntu/Debian 的 tmpfs)会定时清空/tmp,锁文件被删后,后续任务误判为“空闲”,并发风险重现 - 推荐用
/var/run/xxx.lock:标准运行时目录,重启清空但日常稳定;或放在项目自有路径下,如/opt/myapp/.lock - flock 会自动创建锁文件,无需提前
touch;进程退出(含崩溃、kill)后内核自动释放锁,无残留风险
加一行日志,让“跳过”可查可控
单纯跳过执行,容易让人误以为 cron 没触发。补一句失败记录,排查效率翻倍:
- 在命令末尾加:
|| echo "$(date): skipped — lock held" >> /var/log/myjob.log - 这样每次因锁被拒,日志里都会留下明确时间戳和原因,一眼区分“调度正常但被拦住”和“cron 本身失效”
哪些做法看着省事,实则埋雷
以下常见方案看似简单,但在生产环境极易引发死锁或永久阻塞:
- 脚本内部自己
touch/rm锁文件:进程异常退出时 trap 不触发,锁残留,后续所有调度全卡死 - 用
pgrep或pidof查进程名判断是否运行:存在毫秒级竞态窗口;且子进程脱离父进程树(如调用wget、docker pull)时完全漏检 - 加
sleep或重试逻辑:任务排队等待,CPU/内存占用翻倍,违背防重初衷,反而加剧系统压力
















