我想检测我的脚本是否已经在运行,所以我使用这:
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
而触发。pidof
checkIfRunning
在这里,你应该写:
checkIfRunning() {
if pidof -o %PPID -x -- "$0" >/dev/null; then
echo >&2 'Already running!'
exit 1
fi
}
构造的退出状态是or部分if
中运行的最后一个命令的退出状态,或者如果没有运行,则不是条件部分的退出状态。then
else
0
一般来说,if
用 and-or 列表替换结构是错误的。除了整体退出状态的问题之外,如果echo
您的 失败pidof && echo && exit 1
,脚本也不会退出。
我认为,一旦您使用函数或除最简单的脚本之外的任何内容,errexit
就应该避免并进行适当的错误处理。
看set -euo pipefail
bash 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
可以替换为true
或return 0
。