有什么快速而简单的方法可以确保在给定时间内只有一个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)。
其他回答
我使用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
在内存中看到进程的存在是很好的(不管进程的状态是什么);但它对我很有用。
在脚本的开头添加这一行
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
这是人类群体的样板代码。
如果需要更多的日志记录,可以使用这个
[ "${FLOCKER}" != "$0" ] && { echo "Trying to start build from queue... "; exec bash -c "FLOCKER='$0' flock -E $E_LOCKED -en '$0' '$0' '$@' || if [ \"\$?\" -eq $E_LOCKED ]; then echo 'Locked.'; fi"; } || echo "Lock is free. Completing."
使用flock工具设置和检查锁。 这段代码通过检查FLOCKER变量来检测它是否第一次运行,如果它没有设置为脚本名称,那么它会尝试再次递归地使用flock启动脚本,并初始化FLOCKER变量,如果FLOCKER设置正确,那么在前一次迭代中flock成功,可以继续。如果锁繁忙,它将失败,并使用可配置的退出代码。
它似乎不能在Debian 7上工作,但似乎可以在实验util-linux 2.25包上再次工作。上面写着“羊群:……文本文件繁忙”。可以通过禁用脚本上的写权限来覆盖它。
测试“锁文件”是否存在的简单方法是有缺陷的。
为什么?因为它们不会检查文件是否存在,也不会在单个原子操作中创建文件。因为这个;有一个竞争条件会使你的互斥失败。
相反,您可以使用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)。
为什么我们不用像这样的东西
pgrep -f $cmd || $cmd
又快又脏?
#!/bin/sh
if [ -f sometempfile ]
echo "Already running... will now terminate."
exit
else
touch sometempfile
fi
..do what you want here..
rm sometempfile