但是,ps
由于我一直看到人们在使用它们,所以我想讨论一些使用的各种破碎的和半可行的方法,以及它们的许多警告。
这个答案实际上是“为什么不使用并处理外壳中的锁定?”的答案。ps
grep
坏方法#1
首先,在另一个答案中给出的一种方法尽管没有(而且永远不可能)有效并且显然从未经过测试,但还是有一些缺点:
running_proc=$(ps -C bash -o pid=,cmd= | grep my_script);
if [[ "$running_proc" != "$$ bash my_script" ]]; do
echo Already locked
exit 6
fi
让我们修复语法错误和无效的ps
参数,然后获取:
running_proc=$(ps -C bash -o pid,cmd | grep "$0");
echo "$running_proc"
if [[ "$running_proc" != "$$ bash $0" ]]; then
echo Already locked
exit 6
fi
无论您如何运行,此脚本始终总是退出6。
如果使用来运行它./myscript
,那么ps
输出将仅为12345 -bash
,它与所需的字符串不匹配12345 bash ./myscript
,因此将失败。
如果使用来运行它bash myscript
,事情将会变得更加有趣。bash进程派生运行管道,子外壳运行ps
和grep
。原始外壳程序和子外壳程序都将显示在ps
输出中,如下所示:
25793 bash myscript
25795 bash myscript
这不是预期的输出$$ bash $0
,因此您的脚本将退出。
坏办法#2
现在,对于写了坏方法#1的用户来说,很公平,当我第一次尝试这样做时,我做了类似的事情:
if otherpids="$(pgrep -f "$0" | grep -vFx "$$")" ; then
echo >&2 "There are other copies of the script running; exiting."
ps >&2 -fq "${otherpids//$'\n'/ }" # -q takes about a tenth the time as -p
exit 1
fi
这几乎可行。但是,分叉运行管道的事实使这一点不成立。因此,这也将始终存在。
不可靠的方法#3
pids_this_script="$(pgrep -f "$0")"
if not_this_process="$(echo "$pids_this_script" | grep -vFx "$$")"; then
echo >&2 "There are other copies of this script running; exiting."
ps -fq "${not_this_process//$'\n'/ }"
exit 1
fi
此版本通过首先获取在其命令行参数中包含当前脚本的所有PID,然后分别过滤该pidlist来忽略当前脚本的PID,从而避免了方法2中的管道分叉问题。
如果没有其他进程具有与匹配的命令行$0
,并且始终以相同的方式调用脚本(例如,如果使用相对路径然后使用绝对路径调用脚本,则后者不会注意到前者),这可能会起作用)。
不可靠的方法#4
那么,如果我们跳过检查完整的命令行(因为这可能并不表示脚本实际上正在运行),lsof
而是查找所有已打开此脚本的进程,该怎么办?
好吧,是的,这种方法实际上还不错:
if otherpids="$(lsof -t "$0" | grep -vFx "$$")"; then
echo >&2 "Error: There are other processes that have this script open - most likely other copies of the script running. Exiting to avoid conflicts."
ps >&2 -fq "${otherpids//$'\n'/ }"
exit 1
fi
当然,如果脚本的副本正在运行,则新实例将很好地启动,并且您将有两个副本在运行。
或者,如果修改了正在运行的脚本(例如,用Vim或使用git checkout
),则该脚本的“新”版本将毫无问题地启动,因为Vim和都会git checkout
产生一个新文件(新inode)来代替该脚本。旧的。
但是,如果脚本从未被修改且从未被复制过,则此版本相当不错。没有竞争条件,因为在可以进行检查之前必须已打开脚本文件。
如果另一个进程打开了脚本文件,仍然会有误报,但是请注意,即使打开了该文件以便在Vim中进行编辑,vim并不会真正保持脚本文件打开,因此不会导致误报。
但是请记住,如果脚本可能会被编辑或复制,则不要使用这种方法,因为这样会得到假阴性,即一次运行多个实例-因此,用Vim编辑不会产生假阳性的事实不重要给你。不过,我提到了它,因为如果您使用Vim打开脚本,则方法3 确实给出了误报(即拒绝启动)。
那该怎么办呢?
该问题的最高投票答案是一个很好的坚实方法。
也许您可以编写一个更好的方法...但是,如果您不了解上述所有方法的所有问题和注意事项,则不太可能编写一种避免所有这些方法的锁定方法。
kill
ed 时删除其锁定文件。而且最好将自己的pid存储在锁文件中,而不是仅仅触摸它。