有什么快速而简单的方法可以确保在给定时间内只有一个shell脚本实例在运行?
当前回答
下面是一种方法,它结合了原子目录锁定和通过PID检查过期锁,如果过期就重新启动。此外,这并不依赖于任何羞怯。
#!/bin/dash
SCRIPTNAME=$(basename $0)
LOCKDIR="/var/lock/${SCRIPTNAME}"
PIDFILE="${LOCKDIR}/pid"
if ! mkdir $LOCKDIR 2>/dev/null
then
# lock failed, but check for stale one by checking if the PID is really existing
PID=$(cat $PIDFILE)
if ! kill -0 $PID 2>/dev/null
then
echo "Removing stale lock of nonexistent PID ${PID}" >&2
rm -rf $LOCKDIR
echo "Restarting myself (${SCRIPTNAME})" >&2
exec "$0" "$@"
fi
echo "$SCRIPTNAME is already running, bailing out" >&2
exit 1
else
# lock successfully acquired, save PID
echo $$ > $PIDFILE
fi
trap "rm -rf ${LOCKDIR}" QUIT INT TERM EXIT
echo hello
sleep 30s
echo bye
其他回答
PID和锁文件绝对是最可靠的。当您尝试运行程序时,它可以检查锁文件,如果它存在,它可以使用ps查看进程是否仍在运行。如果不是,脚本可以启动,将锁文件中的PID更新为自己的PID。
你需要一个原子操作,比如flock,否则最终会失败。
但是如果没有羊群,该怎么办呢?这是mkdir。这也是一个原子操作。只有一个进程会成功执行mkdir,其他进程都会失败。
所以代码是:
if mkdir /var/lock/.myscript.exclusivelock
then
# do stuff
:
rmdir /var/lock/.myscript.exclusivelock
fi
你需要处理陈旧的锁,否则崩溃后你的脚本将永远不会再次运行。
信号量实用程序使用flock(如上所述,例如by presto8)来实现计数信号量。它支持您想要的任意数量的并发进程。我们使用它来限制各种队列工作者进程的并发级别。
它很像sem,但重量轻得多。(完全披露:我是在发现sem对我们的需求来说太繁重了,而且没有一个简单的计数信号量实用程序可用后写的。)
迟来的派对,使用来自@Majal的想法,这是我的脚本,只启动emacsclient GUI的一个实例。有了它,我可以设置快捷键打开或跳回相同的瘦弱。我有另一个脚本调用emacsclient在终端当我需要它。在这里使用emacsclient只是为了展示一个实际的例子,人们可以选择其他的东西。对于我的小脚本来说,这种方法足够快、足够好。告诉我哪里脏:)
#!/bin/bash
# if [ $(pgrep -c $(basename $0)) -lt 2 ]; then # this works but requires script name to be unique
if [ $(pidof -x "$0"|wc -w ) -lt 3 ]; then
echo -e "Starting $(basename $0)"
emacsclient --alternate-editor="" -c "$@"
else
echo -e "$0 is running already"
fi
这个例子是在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计数。和数字比较。这是复杂而不确定的