在函数中运行 pidof

在函数中运行 pidof

我想检测我的脚本是否已经在运行,所以我使用

pidof -o %PPID -x "$0" >/dev/null && echo 'already running!' && exit 1

这样可行。但我希望它在一个函数中:

#!bin/bash
set -eu
set -o pipefail

checkIfRunning() {
  pidof -o %PPID -x "$0" >/dev/null && echo 'already running!' && exit 1
}

#...

checkIfRunning

这是行不通的:即使只有一个脚本实例正在运行,它也总是会退出。

我认为如果pidof在子 shell 中运行,它会检测子 shell 和脚本,从而解释结果 - 但我不明白这里是如何创建子 shell 的?

这是什么原因,我该如何解决?

答案1

在您的代码中,如果未处理的命令失败set -e(缩写为set -o errexit),则会导致 shell 退出。

pidof当找不到匹配的进程时,返回失败退出状态,但您确实处理了该失败。pidof用作和-或列表,所以它的失败确实不是扳机errexit

但是因为你使用了和-或列表在您应该使用if构造的地方,其pidof && othercommand本身以非零退出状态退出,并且因为该和-或列表是checkIfRunning函数中运行的最后一个事物,所以checkIfRunning函数本身也将返回失败退出状态。

并且该故障未被处理,因此errexit被触发。不是因为失败而是因为失败errexit而触发。pidofcheckIfRunning

在这里,你应该写:

checkIfRunning() {
  if pidof -o %PPID -x -- "$0" >/dev/null; then
    echo >&2 'Already running!'
    exit 1
  fi
}

构造的退出状态是or部分if中运行的最后一个命令的退出状态,或者如果没有运行,则不是条​​件部分的退出状态。thenelse0

一般来说,if用 and-or 列表替换结构是错误的。除了整体退出状态的问题之外,如果echo您的 失败pidof && echo && exit 1,脚本也不会退出。

我认为,一旦您使用函数或除最简单的脚本之外的任何内容,errexit就应该避免并进行适当的错误处理。

set -euo pipefailbash wiki 中的相应部分( 的缩写set -o errexit -o nounset -o pipefail)或此常见问题解答条目

关于您的(现已删除)答案它试图解决这个问题:

checkIfRunning() {
  set +e                              # <---
  pidof -o %PPID -x $0 >/dev/null \
    && echo "running!" && exit 1
  set -e                              # <---
}

它起作用的唯一原因是因为set -e碰巧以成功状态退出,所以checkIfRunning也是如此,set +e是不相关且不需要的,set -e可以替换为truereturn 0

相关内容