有什么快速而简单的方法可以确保在给定时间内只有一个shell脚本实例在运行?
当前回答
测试“锁文件”是否存在的简单方法是有缺陷的。
为什么?因为它们不会检查文件是否存在,也不会在单个原子操作中创建文件。因为这个;有一个竞争条件会使你的互斥失败。
相反,您可以使用mkdir。Mkdir创建一个目录,如果它还不存在,如果它存在,它设置一个退出码。更重要的是,它在一个原子动作中完成了所有这些操作,这使得它非常适合这个场景。
if ! mkdir /tmp/myscript.lock 2>/dev/null; then
echo "Myscript is already running." >&2
exit 1
fi
有关所有细节,请参阅优秀的BashFAQ: http://mywiki.wooledge.org/BashFAQ/045
如果你想要处理陈旧的锁,fuser(1)会派上用场。唯一的缺点是这个操作大约需要一秒钟,所以它不是即时的。
下面是我曾经写过的一个函数,它使用fuser解决了这个问题:
# mutex file
#
# Open a mutual exclusion lock on the file, unless another process already owns one.
#
# If the file is already locked by another process, the operation fails.
# This function defines a lock on a file as having a file descriptor open to the file.
# This function uses FD 9 to open a lock on the file. To release the lock, close FD 9:
# exec 9>&-
#
mutex() {
local file=$1 pid pids
exec 9>>"$file"
{ pids=$(fuser -f "$file"); } 2>&- 9>&-
for pid in $pids; do
[[ $pid = $$ ]] && continue
exec 9>&-
return 1 # Locked by a pid.
done
}
你可以像这样在脚本中使用它:
mutex /var/run/myscript.lock || { echo "Already running." >&2; exit 1; }
如果您不关心可移植性(这些解决方案应该适用于几乎任何UNIX机器),Linux的fuser(1)提供了一些额外的选项,还有flock(1)。
其他回答
试试下面的方法,
ab=`ps -ef | grep -v grep | grep -wc processname`
然后使用if循环将变量与1匹配。
这个例子是在man flock中解释的,但它需要一些改进,因为我们应该管理bug和退出代码:
#!/bin/bash
#set -e this is useful only for very stupid scripts because script fails when anything command exits with status more than 0 !! without possibility for capture exit codes. not all commands exits >0 are failed.
( #start subprocess
# Wait for lock on /var/lock/.myscript.exclusivelock (fd 200) for 10 seconds
flock -x -w 10 200
if [ "$?" != "0" ]; then echo Cannot lock!; exit 1; fi
echo $$>>/var/lock/.myscript.exclusivelock #for backward lockdir compatibility, notice this command is executed AFTER command bottom ) 200>/var/lock/.myscript.exclusivelock.
# Do stuff
# you can properly manage exit codes with multiple command and process algorithm.
# I suggest throw this all to external procedure than can properly handle exit X commands
) 200>/var/lock/.myscript.exclusivelock #exit subprocess
FLOCKEXIT=$? #save exitcode status
#do some finish commands
exit $FLOCKEXIT #return properly exitcode, may be usefull inside external scripts
你可以用另一种方法,列出我过去用过的过程。但这比上面的方法要复杂得多。你应该按ps列出进程,按其名称过滤,附加过滤器grep -v grep清除寄生虫,最后按grep -c计数。和数字比较。这是复杂而不确定的
看看FLOM (Free LOck Manager) http://sourceforge.net/projects/flom/:,您可以使用文件系统中不需要锁文件的抽象资源来同步命令和/或脚本。您可以在没有NFS(网络文件系统)服务器这样的NAS(网络附加存储)的情况下同步在不同系统中运行的命令。
使用最简单的用例,序列化“command1”和“command2”可能和执行一样简单:
flom -- command1
and
flom -- command2
来自两个不同的shell脚本。
信号量实用程序使用flock(如上所述,例如by presto8)来实现计数信号量。它支持您想要的任意数量的并发进程。我们使用它来限制各种队列工作者进程的并发级别。
它很像sem,但重量轻得多。(完全披露:我是在发现sem对我们的需求来说太繁重了,而且没有一个简单的计数信号量实用程序可用后写的。)
我使用onlineer @脚本的开头:
#!/bin/bash
if [[ $(pgrep -afc "$(basename "$0")") -gt "1" ]]; then echo "Another instance of "$0" has already been started!" && exit; fi
.
the_beginning_of_actual_script
在内存中看到进程的存在是很好的(不管进程的状态是什么);但它对我很有用。