有什么快速而简单的方法可以确保在给定时间内只有一个shell脚本实例在运行?
当前回答
上面有很多很好的答案。你也可以使用dotlockfile。
这是一些你可以在你的脚本中使用的示例代码:
$LOCKFILENAME=/var/run/test.lock
if ! dotlockfile -l -p -r 2 $LOCKFILENAME
then
echo "This test process already running!"
exit 1
fi
其他回答
另一个选项是通过运行set -C来使用shell的noclobber选项。如果文件已经存在,那么>将失败。
简而言之:
set -C
lockfile="/tmp/locktest.lock"
if echo "$$" > "$lockfile"; then
echo "Successfully acquired lock"
# do work
rm "$lockfile" # XXX or via trap - see below
else
echo "Cannot acquire lock - already locked by $(cat "$lockfile")"
fi
这会导致shell调用:
open(pathname, O_CREAT|O_EXCL)
自动创建文件,如果文件已经存在则失败。
根据BashFAQ 045上的评论,这可能在ksh88中失败,但它在我所有的shell中都有效:
$ strace -e trace=creat,open -f /bin/bash /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_LARGEFILE, 0666) = 3
$ strace -e trace=creat,open -f /bin/zsh /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_LARGEFILE, 0666) = 3
$ strace -e trace=creat,open -f /bin/pdksh /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_TRUNC|O_LARGEFILE, 0666) = 3
$ strace -e trace=creat,open -f /bin/dash /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_LARGEFILE, 0666) = 3
有趣的是pdksh添加了O_TRUNC标志,但显然这是多余的: 要么创建一个空文件,要么什么也不做。
如何进行rm取决于您希望如何处理不干净的出口。
在干净退出时删除
新的运行失败,直到导致不干净退出的问题得到解决,并手动删除锁文件。
# acquire lock
# do work (code here may call exit, etc.)
rm "$lockfile"
在任何出口删除
只要脚本尚未运行,新的运行就会成功。
trap 'rm "$lockfile"' EXIT
你需要一个原子操作,比如flock,否则最终会失败。
但是如果没有羊群,该怎么办呢?这是mkdir。这也是一个原子操作。只有一个进程会成功执行mkdir,其他进程都会失败。
所以代码是:
if mkdir /var/lock/.myscript.exclusivelock
then
# do stuff
:
rmdir /var/lock/.myscript.exclusivelock
fi
你需要处理陈旧的锁,否则崩溃后你的脚本将永远不会再次运行。
使用进程的锁更强大,还可以处理不合理的退出。 只要进程在运行,Lock_file就保持打开状态。一旦进程存在,它将被关闭(通过shell)(即使它被杀死)。 我发现这个方法非常有效:
lock_file=/tmp/`basename $0`.lock
if fuser $lock_file > /dev/null 2>&1; then
echo "WARNING: Other instance of $(basename $0) running."
exit 1
fi
exec 3> $lock_file
这个例子是在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计数。和数字比较。这是复杂而不确定的
创建一个锁定文件在一个已知的位置,并检查是否存在脚本启动?如果有人试图追踪阻止脚本执行的错误实例,那么将PID放在文件中可能会有帮助。