flock加锁最简写法是用flock -n /tmp/myjob.lock -c 'your_command'包裹脚本逻辑,或长期运行时用exec 200>/tmp/myjob.lock && flock -n 200 || exit 1;锁文件须置于本地磁盘可信路径,避免/tmp或NFS;ps grep防重不可靠,因存在竞态条件。

用 flock 给脚本加锁最简写法
直接在脚本开头加一行 flock,就能拦住重复执行。它不依赖进程名或 PID 文件,靠内核级文件描述符锁,可靠得多。
常见错误是只对某个文件加锁,但没让锁作用到整个脚本执行过程——结果锁住了文件,脚本却提前退出,后续实例照样进来。
- 正确姿势:用
flock包裹整个脚本逻辑,推荐用flock -n /tmp/myjob.lock -c 'your_command' - 如果脚本本身要长期运行(比如守护任务),改用文件描述符方式:
exec 200>/tmp/myjob.lock && flock -n 200 || exit 1,这样锁会随脚本生命周期自动释放 -
-n表示“非阻塞”,失败直接退出;去掉就是阻塞等待,慎用,容易卡住调度器
flock 锁文件路径选哪里才安全
锁文件不能放在 /tmp 下就万事大吉——有些系统会定期清空 /tmp,或者挂载了 noexec/nodev,导致 flock 失效(不是报错,而是悄悄不生效)。
更麻烦的是 NFS:默认不支持 flock,跨机器时锁完全失效,但又不报错,非常隐蔽。
- 优先选本地磁盘上的路径,比如
/var/run/myapp.lock或/opt/myapp/.lock - 确保目录存在且当前用户有读写权限(
flock需要能 open+write 锁文件) - 绝对不要用
/tmp下带随机后缀的路径(如/tmp/myjob.$$.lock),每次都是新文件,根本锁不住
为什么 ps aux | grep script.sh 不是靠谱的防重方案
因为竞态条件太明显:你查完没进程,下一毫秒另一个实例就启动了,两个脚本同时跑起来。
而且 grep 自身会出现在 ps 结果里,容易误判;脚本名重复、参数含空格、被 bash -c 包裹等情况都会漏判或误杀。
-
flock是原子操作,内核保证同一时刻最多一个进程拿到锁 - 不用解析进程列表,不依赖 ps 输出格式,也不怕 shell wrapper 干扰
- 如果必须兼容旧环境(比如某些容器里没
flock),可用set -C; > /tmp/lockfile 2>/dev/null || exit 1配合trap清理,但可靠性远不如flock
调试 flock 失败时该看什么
最常遇到的是“明明加了锁,还是双开”,十有八九是锁文件路径不一致,或者脚本里某处 fork 出子进程后锁被继承/丢失了。
flock 锁绑定在文件描述符上,不是文件路径;子进程默认继承 fd,但 exec 后通常就丢了——所以别指望后台子任务还受同一把锁保护。
- 检查实际执行命令是否和加锁命令是同一个 shell 进程(比如用了
&放后台,锁就不管用了) - 用
lsof /path/to/lockfile看当前谁占着锁,注意输出里的DEL标记表示文件已被 unlink 但 fd 仍打开 - 错误信息只有两种:
flock: cannot open lock file(权限/路径问题)和flock: no locks available(NFS 或已达到系统 limit)
真正难搞的是锁文件被意外删除又重建——flock 不认文件名,只认 inode,删了再新建就是新锁,老锁还在老 inode 上挂着。这种得靠运维规范,代码里很难兜底。

















