我的脚本不能同时运行多次。因此,它将创建一个锁定文件,并在退出前将其删除。在开始工作之前,它会检查锁定文件是否不存在。
一种非常常见的锁定方法是something like this:
function setupLockFile() {
if (set -o noclobber; echo "lock" > "$lockfile") 2>/dev/null; then
trap "rm -f $lockfile; exit $?" INT TERM EXIT
else
echo "Script running... exiting!"
exit 1
fi
}
但是存在竞争条件-if
将创建文件(如果不存在),并且可以在定义trap
之前终止脚本。这样,锁定文件将不会被删除。
那么安全的方法是什么?
答案 0 :(得分:3)
那不是比赛-它是失败的韧性。如果脚本在删除文件之前死掉,则需要手动清理。
尝试自动执行此清除操作的通常方法是从任何现有文件中读取PID,进行测试以查看该进程是否仍然存在,如果不存在,则基本上忽略其存在。不幸的是,由于没有原子的比较和设置操作,要进行正确的操作并非易事,因为它引入了新的竞争,在读取PID和其他试图忽略其存在的人之间。
查看this question,了解有关仅使用文件系统进行锁定的更多想法。
我的建议是将锁定文件存储在临时文件系统中(/var/run
通常是tmpfs,以允许pidfile在重新启动时安全消失),以便在重新启动后自行修复,或者让脚本抛出该错误。双手并要求人工干预。可靠地处理每一个失败案例都会增加复杂性,因此可能比寻求人工帮助带来更多的失败可能性。
复杂性不仅限于今天,还涉及代码的生命周期。完成后可能是正确的,但是下一个人会破坏它吗?
答案 1 :(得分:1)
让我们尝试另一种方法:
例如:
trap "cleanUp" INT TERM EXIT
function cleanUp {
if [[ $$ -eq $(<$lockfile) ]]; then
rm -f $lockfile
exit $?
fi
}
function setupLockFile {
if ! (set -o noclobber; echo "$$" > "$lockfile") 2>/dev/null; then
echo "Script running... exiting!"
exit 1
fi
}
这样,您就可以将锁定文件的存在及其创建检查作为单个操作进行保留,同时还可以防止陷阱删除先前正在运行的实例的锁定文件。
此外,正如我在下面的评论中提到的,如果锁定文件已经存在,我建议检查具有给定PID的进程是否正在运行。 因为您永远不知道由于某种原因,锁定文件仍然可以在磁盘上保持孤立状态。 因此,如果您希望减轻手动删除孤立锁线的需求,则可以添加其他逻辑来检查PID是否为孤立的。
例如-如果未找到来自锁定文件的给定PID的正在运行的进程,则可以假定这是来自先前实例的孤立的锁定文件,然后可以用当前PID覆盖它并继续。 如果找到了进程,则可以比较其名称以查看它是否确实是同一脚本的另一个实例-如果找不到,则可以覆盖锁定文件中的PID并继续。
为了简化起见,我没有在代码中包括它,如果需要,您可以尝试自己创建此逻辑。 :)
答案 2 :(得分:0)
首先检查锁文件,然后陷阱,然后将其写入:
function setupLockFile() {
if [ -f "$lockfile" ]; then
echo "Script running... exiting!"
exit 1
else trap "rm -f $lockfile; exit $?" INT TERM EXIT
set -o noclobber; echo "lock" > "$lockfile" || exit 1
fi
}
还有一种“官方”方式,可以使用flock命令来检查锁文件,该命令是util-linux的一部分。