有什么快速而简单的方法可以确保在给定时间内只有一个shell脚本实例在运行?
当前回答
信号量实用程序使用flock(如上所述,例如by presto8)来实现计数信号量。它支持您想要的任意数量的并发进程。我们使用它来限制各种队列工作者进程的并发级别。
它很像sem,但重量轻得多。(完全披露:我是在发现sem对我们的需求来说太繁重了,而且没有一个简单的计数信号量实用程序可用后写的。)
其他回答
为什么我们不用像这样的东西
pgrep -f $cmd || $cmd
我想要去掉锁文件,锁dirs,特殊的锁程序,甚至pidof,因为它不是在所有的Linux安装中都能找到的。还希望有尽可能简单的代码(或至少尽可能少的行)。最简单的if语句,在一行中:
if [[ $(ps axf | awk -v pid=$$ '$1!=pid && $6~/'$(basename $0)'/{print $1}') ]]; then echo "Already running"; exit; fi
为了使锁定可靠,您需要一个原子操作。以上许多建议 不是原子的。建议的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放在同一个目录下。
下面是一个使用锁文件并将PID回显到锁文件中的实现。如果进程在删除pidfile之前被杀死,这可以起到保护作用:
LOCKFILE=/tmp/lock.txt
if [ -e ${LOCKFILE} ] && kill -0 `cat ${LOCKFILE}`; then
echo "already running"
exit
fi
# make sure the lockfile is removed when we exit and then claim it
trap "rm -f ${LOCKFILE}; exit" INT TERM EXIT
echo $$ > ${LOCKFILE}
# do stuff
sleep 1000
rm -f ${LOCKFILE}
这里的技巧是kill -0,它不传递任何信号,只是检查具有给定PID的进程是否存在。此外,调用trap将确保即使进程被杀死(kill -9除外),锁文件也会被移除。
我使用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
在内存中看到进程的存在是很好的(不管进程的状态是什么);但它对我很有用。