有什么快速而简单的方法可以确保在给定时间内只有一个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

其他回答

为了使锁定可靠,您需要一个原子操作。以上许多建议 不是原子的。建议的lockfile(1)实用程序作为手册页看起来很有前途 提到,它是“抗nfs”的。如果您的操作系统不支持lockfile(1)和 您的解决方案必须在NFS上工作,您没有太多的选项....

NFSv2有两个原子操作:

符号链接 重命名

在NFSv3中,create调用也是原子的。

目录操作在NFSv2和NFSv3下不是原子的(请参考Brent Callaghan的书“NFS Illustrated”,ISBN 0-201-32570-5;Brent是Sun的nfs老手)。

知道了这一点,你可以为文件和目录实现自旋锁(在shell中,而不是PHP中):

锁定当前目录:

while ! ln -s . lock; do :; done

锁定文件:

while ! ln -s ${f} ${f}.lock; do :; done

解锁当前目录(假设正在运行的进程真的获得了锁):

mv lock deleteme && rm deleteme

解锁文件(假设正在运行的进程真的获得了锁):

mv ${f}.lock ${f}.deleteme && rm ${f}.deleteme

Remove也不是原子的,因此首先是rename(它是原子的),然后是Remove。

对于符号链接和重命名调用,两个文件名必须驻留在同一个文件系统上。我的建议是:只使用简单的文件名(没有路径),把file和lock放在同一个目录下。

又快又脏?

#!/bin/sh

if [ -f sometempfile ]
  echo "Already running... will now terminate."
  exit
else
  touch sometempfile
fi

..do what you want here..

rm sometempfile

信号量实用程序使用flock(如上所述,例如by presto8)来实现计数信号量。它支持您想要的任意数量的并发进程。我们使用它来限制各种队列工作者进程的并发级别。

它很像sem,但重量轻得多。(完全披露:我是在发现sem对我们的需求来说太繁重了,而且没有一个简单的计数信号量实用程序可用后写的。)

羊群走的路才是正确的。想想当脚本突然失效时会发生什么。在羊群的情况下,你只是松散的羊群,但这不是一个问题。另外,请注意,一个邪恶的伎俩是在脚本本身取一群。但这当然会让您完全陷入权限问题。

创建一个锁定文件在一个已知的位置,并检查是否存在脚本启动?如果有人试图追踪阻止脚本执行的错误实例,那么将PID放在文件中可能会有帮助。