有什么快速而简单的方法可以确保在给定时间内只有一个shell脚本实例在运行?
当前回答
一些unix具有与前面提到的flock非常相似的lockfile。
从手册中:
Lockfile可以用来创建一个 或者更多的信号量文件。如果锁, 文件不能创建所有指定的 文件(在指定的顺序),它 等待睡眠时间(默认为8) 秒并重试最后一个文件 没有成功。您可以指定 直到重试的次数 返回失败。如果数字 重试次数为-1(默认值,即 -r-1)锁文件将永远重试。
其他回答
在flock(2)系统调用周围有一个包装器,毫无想象力地称为flock(1)。这使得可靠地获得独占锁相对容易,而不必担心清理等问题。手册页上有一些关于如何在shell脚本中使用它的示例。
下面是一个使用锁文件并将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除外),锁文件也会被移除。
如果您不想或不能使用flock(例如,您没有使用共享文件系统),请考虑使用外部服务,如lockable。
它暴露了咨询锁原语,就像flock一样。特别地,你可以通过以下方式获取锁:
https://lockable.dev/api/acquire/my-lock-name
然后通过
https://lockable.dev/api/release/my-lock-name
通过将脚本执行与锁获取和释放结合在一起,您可以确保在任何给定时间只有一个流程实例在运行。
一个有flock(1)但没有subshell的例子。Flock ()ed文件/tmp/foo永远不会被删除,但这没关系,因为它会被Flock()和un-flock()ed。
#!/bin/bash
exec 9<> /tmp/foo
flock -n 9
RET=$?
if [[ $RET -ne 0 ]] ; then
echo "lock failed, exiting"
exit
fi
#Now we are inside the "critical section"
echo "inside lock"
sleep 5
exec 9>&- #close fd 9, and release lock
#The part below is outside the critical section (the lock)
echo "lock released"
sleep 5
这个我在任何地方都没有发现,它使用read,我不确切地知道read是否实际上是原子的,但到目前为止它对我来说很有用……它很有趣,因为它只是bash内置的,这是一个进程内实现,你启动locker协进程并使用它的I / O来管理锁,同样可以在进程间完成,只需将目标I / O从locker文件描述符交换到文件系统上的文件描述符(exec 3<>/file && exec 4</file)
## gives locks
locker() {
locked=false
while read l; do
case "$l" in
lock)
if $locked; then
echo false
else
locked=true
echo true
fi
;;
unlock)
if $locked; then
locked=false
echo true
else
echo false
fi
;;
*)
echo false
;;
esac
done
}
## locks
lock() {
local response
echo lock >&${locker[1]}
read -ru ${locker[0]} response
$response && return 0 || return 1
}
## unlocks
unlock() {
local response
echo unlock >&${locker[1]}
read -ru ${locker[0]} response
$response && return 0 || return 1
}