如何确保只有一个 bash 脚本实例运行?

Tob*_*ler 38 linux bash lock

不需要额外工具的解决方案将是首选。

Bru*_*ger 23

几乎就像 nsg 的回答:使用锁定目录。在 linux、unix 和 *BSD 以及许多其他操作系统下,目录创建是原子的。

if mkdir $LOCKDIR
then
    # Do important, exclusive stuff
    if rmdir $LOCKDIR
    then
        echo "Victory is mine"
    else
        echo "Could not remove lock dir" >&2
    fi
else
    # Handle error condition
    ...
fi
Run Code Online (Sandbox Code Playgroud)

您可以将锁定 sh 的 PID 放入锁定目录中的一个文件中以进行调试,但不要陷入认为可以检查该 PID 以查看锁定过程是否仍然执行的陷阱。许多比赛条件都在这条道路上。


小智 21

要添加到Bruce Ediger 的答案中,并受到此答案的启发,您还应该为清理添加更多智能以防止脚本终止:

#Remove the lock directory
function cleanup {
    if rmdir $LOCKDIR; then
        echo "Finished"
    else
        echo "Failed to remove lock directory '$LOCKDIR'"
        exit 1
    fi
}

if mkdir $LOCKDIR; then
    #Ensure that if we "grabbed a lock", we release it
    #Works for SIGTERM and SIGINT(Ctrl-C)
    trap "cleanup" EXIT

    echo "Acquired lock, running"

    # Processing starts here
else
    echo "Could not create lock directory '$LOCKDIR'"
    exit 1
fi
Run Code Online (Sandbox Code Playgroud)


Set*_*thu 13

确保运行单个 bash 脚本实例的另一种方法:

#!/bin/bash

# Check if another instance of script is running
pidof -o %PPID -x $0 >/dev/null && echo "ERROR: Script $0 already running" && exit 1

...
Run Code Online (Sandbox Code Playgroud)

pidof -o %PPID -x $0 如果现有脚本已在运行,则获取现有脚本的 PID;如果没有其他脚本正在运行,则以错误代码 1 退出


ter*_*don 7

这可能太简单了,如果我错了,请纠正我。还ps不够简单?

#!/bin/bash 

me="$(basename "$0")";
running=$(ps h -C "$me" | grep -wv $$ | wc -l);
[[ $running > 1 ]] && exit;

# do stuff below this comment
Run Code Online (Sandbox Code Playgroud)

  • 使用此解决方案,如果同一脚本的两个实例同时启动,则它们有可能“看到”彼此并且都将终止。这可能不是问题,但也可能是问题,只要意识到这一点。 (3认同)
  • 我已经使用这种情况一个星期了,有 2 次它并没有阻止新进程的启动。我想出了问题所在 - 新 pid 是旧 pid 的子字符串,并被 `grep -v $$` 隐藏。实际示例:旧 - 14532,新 - 1453,旧 - 28858,新 - 858。 (2认同)
  • 我通过将 `grep -v $$` 更改为 `grep -v "^${$} "` 来修复它 (2认同)

sch*_*pel 6

尽管您已经要求无需额外工具的解决方案,但这是我最喜欢的使用方式flock

#!/bin/sh

[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :

echo "servus!"
sleep 10
Run Code Online (Sandbox Code Playgroud)

这来自 的示例部分man flock,它进一步解释了:

这是 shell 脚本的有用样板代码。把它放在你想要锁定的 shell 脚本的顶部,它会在第一次运行时自动锁定自己。如果 env var $FLOCKER 未设置为正在运行的 shell 脚本,则在使用正确的参数重新执行之前执行 flock 并获取一个排他非阻塞锁(使用脚本本身作为锁文件)。它还将 FLOCKER env var 设置为正确的值,因此它不会再次运行。

需要考虑的要点:

更新:如果您的脚本可能通过不同的路径(例如,通过其绝对或相对路径)被$0调用,或者换句话说,如果在并行调用中不同,那么还需要使用realpath

[ "${FLOCKER}" != "`realpath '$0'`" ] && exec env FLOCKER="`realpath '$0'`" flock -en "$0" "$0" "$@" || :
Run Code Online (Sandbox Code Playgroud)


Joh*_*Doe 5

这是Anselmo 的答案的修改版本。这个想法是使用 bash 脚本本身创建一个只读文件描述符并用于flock处理锁。

script=`realpath $0`     # get absolute path to the script itself
exec 6< "$script"        # open bash script using file descriptor 6
flock -n 6 || { echo "ERROR: script is already running" && exit 1; }   # lock file descriptor 6 OR show error message if script is already running

echo "Run your single instance code here"
Run Code Online (Sandbox Code Playgroud)

与所有其他答案的主要区别在于,此代码不会修改文件系统,占用空间非常小,并且不需要任何清理,因为一旦脚本完成独立于退出状态,文件描述符就会关闭。因此,脚本失败或成功并不重要。