有什么快速而简单的方法可以确保在给定时间内只有一个shell脚本实例在运行?


当前回答

一些unix具有与前面提到的flock非常相似的lockfile。

从手册中:

Lockfile可以用来创建一个 或者更多的信号量文件。如果锁, 文件不能创建所有指定的 文件(在指定的顺序),它 等待睡眠时间(默认为8) 秒并重试最后一个文件 没有成功。您可以指定 直到重试的次数 返回失败。如果数字 重试次数为-1(默认值,即 -r-1)锁文件将永远重试。

其他回答

如果您不想或不能使用flock(例如,您没有使用共享文件系统),请考虑使用外部服务,如lockable。

它暴露了咨询锁原语,就像flock一样。特别地,你可以通过以下方式获取锁:

https://lockable.dev/api/acquire/my-lock-name

然后通过

https://lockable.dev/api/release/my-lock-name

通过将脚本执行与锁获取和释放结合在一起,您可以确保在任何给定时间只有一个流程实例在运行。

我想要去掉锁文件,锁dirs,特殊的锁程序,甚至pidof,因为它不是在所有的Linux安装中都能找到的。还希望有尽可能简单的代码(或至少尽可能少的行)。最简单的if语句,在一行中:

if [[ $(ps axf | awk -v pid=$$ '$1!=pid && $6~/'$(basename $0)'/{print $1}') ]]; then echo "Already running"; exit; fi

在flock(2)系统调用周围有一个包装器,毫无想象力地称为flock(1)。这使得可靠地获得独占锁相对容易,而不必担心清理等问题。手册页上有一些关于如何在shell脚本中使用它的示例。

实际上,尽管bmdhacks的答案几乎很好,但在第一次检查锁文件之后和在写入它之前,第二个脚本有轻微的机会运行。它们都将写入锁文件,它们都将运行。以下是如何确保它有效的方法:

lockfile=/var/lock/myscript.lock

if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null ; then
  trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT
else
  # or you can decide to skip the "else" part if you want
  echo "Another instance is already running!"
fi

如果文件已经存在,noclobber选项将确保重定向命令将失败。因此,重定向命令实际上是原子的—您用一个命令写入和检查文件。你不需要在文件的末尾删除锁文件-它会被陷阱删除。我希望这对以后读到它的人有帮助。

另外,我没有看到Mikel已经正确地回答了这个问题,尽管他没有包括trap命令,以减少使用Ctrl-C停止脚本后留下锁文件的机会。这就是完整的解

这个我在任何地方都没有发现,它使用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
}