【发布时间】:2014-06-02 01:18:30
【问题描述】:
在运行 shell 脚本时,我想绝对确定在给定时间只运行它的一个实例。
我应该采用 文件锁定 机制吗,比如
touch lockfile
do_work
rm lockfile
够了吗?
【问题讨论】:
-
Here's 一种方式。不过我不是很喜欢。
在运行 shell 脚本时,我想绝对确定在给定时间只运行它的一个实例。
我应该采用 文件锁定 机制吗,比如
touch lockfile
do_work
rm lockfile
够了吗?
【问题讨论】:
您可以为此使用flock:
(
flock -s 200
# .... commands executed under lock....
)200>/var/lock/mylockfile
它在 Util-linux 下一代包装下可用... http://en.wikipedia.org/wiki/Util-linux
【讨论】:
flock 在我的系统上似乎不存在。即使是谷歌搜索,我也只发现了sys/file.h clib。你能附上一个链接吗?
由于flock 没有安装在我使用的某些系统上——我经常在 Ubuntu(有它)和 Mac OS X(没有)之间切换——我使用这个简单的框架没有任何实际问题:
LOCK_NAME="MY_GREAT_BASH_SCRIPT"
LOCK_DIR='/tmp/'${LOCK_NAME}.lock
PID_FILE=${LOCK_DIR}'/'${LOCK_NAME}'.pid'
if mkdir ${LOCK_DIR} 2>/dev/null; then
# If the ${LOCK_DIR} doesn't exist, then start working & store the ${PID_FILE}
echo $$ > ${PID_FILE}
echo "Hello world!"
rm -rf ${LOCK_DIR}
exit
else
if [ -f ${PID_FILE} ] && kill -0 $(cat ${PID_FILE}) 2>/dev/null; then
# Confirm that the process file exists & a process
# with that PID is truly running.
echo "Running [PID "$(cat ${PID_FILE})"]" >&2
exit
else
# If the process is not running, yet there is a PID file--like in the case
# of a crash or sudden reboot--then get rid of the ${LOCK_DIR}
rm -rf ${LOCK_DIR}
exit
fi
fi
这个想法是一般核心——我有echo "Hello world!"——是你脚本的核心所在。其余部分基本上是基于mkdir 的锁定机制。对is in this answer概念的很好解释:
mkdir 如果目录不存在,则创建一个目录,如果存在, 它设置了一个退出代码。更重要的是,它在一个单一的 原子动作使其非常适合这种情况。
【讨论】:
它认为你的逻辑更像是:
if [ ! -e lockfile ]; then
touch lockfile
do_work
rm lockfile
fi
但请注意,即使这样还不够。事实上,它为微妙的错误铺平了道路。由于整个事情不是原子的,第二个进程最好在if 子句之后但在touch 之前开始。
你的想法的一个不太天真的实现会尝试获取锁操作的原子性。这可以在 Bash 中完成。
一个可能的解决方案是noclobber 选项,它禁止写入已经存在的文件,从而以更原子的方式完成if 子句和touch 命令。
我的锁定获取代码将如下所示:
if ! ( set -o noclobber; echo > lockfile ) exit
【讨论】:
在方便地在程序的锁定文件中记录 shell 的 PID 时,要么创建锁定文件,要么失败(不可能出现竞争条件)。如果程序收到任何列出的信号,它就会清理
#!/bin/bash
# Garbage clean up with trap given any of these SIGNALS
# INT Pressing Control-C kill -2 pid
# TERM Termination signal kill -15 pid
# EXIT Shell exit
LockFile="/tmp/${0}.lock"
if ( set -o noclobber; echo "$$" > "$LockFile") 2> /dev/null; then
trap 'rm -f "$LockFile"; exit $?' INT TERM EXIT
else
echo -e "Program \"$0\" already running"
exit 1
fi
【讨论】: