如何让 -e
/errexit 在 bash 函数中工作,以便函数中第一个失败的命令* 导致函数返回错误代码(就像 - e
在顶层工作)。
* 不是 bool 表达式的一部分,if/elif/while/etc 等等
我运行了以下测试脚本,我希望名称中包含 f
的任何函数(即源代码中的 false
行)返回错误代码,但如果之后有另一个命令,他们就不会这样做。即使当我将函数体放入再次指定 set -e
的子 shell 中时,它也会在命令失败后盲目地继续执行该函数,而不是使用非零状态代码退出子 shell/函数。
测试的环境/解释器:
我在所有这些 envs/shell 中都得到了相同的结果,我想这是可以预料的,除非有一个有问题。
拱门:
bash test.sh
(bash 5.1)zsh test.sh
(zsh 5.8)sh test.sh
(只是 bash 的符号链接(symbolic link))
Alpine :
docker run -v/test.sh:/test.sh alpine:latest sh/test.sh
(BusyBox 1.34)
Ubuntu:
docker run -v/test.sh:/test.sh ubuntu:21.04 sh/test.sh
docker run -v/test.sh:/test.sh ubuntu:21.04 bash/test.sh
(bash 5.1)docker run -v/test.sh:/test.sh ubuntu:21.04 dash/test.sh
(bash 5.1)
测试脚本
set -e
t() {
true
}
f() {
false
}
tf() {
true
false
}
ft() {
false
true
}
et() {
set -e
true
}
ef() {
set -e
false
}
etf() {
set -e
true
false
}
eft() {
set -e
false
true
}
st() {( set -e
true
)}
sf() {( set -e
false
)}
stf() {( set -e
true
false
)}
sft() {( set -e
false
true
)}
for test in t f tf ft _ et ef etf eft _ st sf stf sft; do
if [ "$test" = '_' ]; then
echo ""
elif "$test"; then
echo "$test: pass"
else
echo "$test: fail"
fi
done
我的机器上的输出
t: pass
f: fail
tf: fail
ft: pass
et: pass
ef: fail
etf: fail
eft: pass
st: pass
sf: fail
stf: fail
sft: pass
期望的输出
无需显着更改函数本身的源代码,即不添加 if/then 或 || return
到函数中的每一行。
t: pass
f: fail
tf: fail
ft: fail
et: pass
ef: fail
etf: fail
eft: fail
st: pass
sf: fail
stf: fail
sft: fail
或者至少在一组中通过/失败/失败/失败,这样我就可以使用这种方法来编写强大的函数。
采用已接受的解决方案
通过接受的解决方案,前四项测试给出了所需的结果。其他两组没有,但无关紧要,因为这些是我自己解决该问题的尝试。根本原因是“当 if/while 条件失败时不要错误退出脚本”行为传播到条件中调用的任何函数中,而不仅仅是应用于最终结果。
最佳答案
How to make errexit behaviour work in bash functions
调用不在 if
内部的函数.
I expect any function containing f in its name (i.e. a false line in its source) to return error-code
Weeeeeell,您的期望与现实不符。这不是它的实现方式。和标志err<b>exit</b>
将退出您的脚本,而不返回错误代码。
Desired output
你可以:
下载 Bash 或其他 shell 源代码,或者编写您自己的源代码并实现您想要的行为。
- 考虑扩展类似
shopt -s errexit_but_return_nonzero_in_if_contexts
的行为或者更短的东西
- 考虑扩展类似
在单独的 shell 中运行它。
elif bash -ec "$(declare -f "$test"); $test"; then
编写自定义可加载文件以“清除”
CMD_IGNORE_RETURN
当 Bash 进入set -e
的上下文时标记应该被忽略。我认为static COMMAND *currently_executing_command;
需要是外部的,然后只是currently_executing_command.flags &= ~CMD_IGNORE_RETURN;
.您可以做一个包装器,在其中临时禁用 errexit 并获取返回状态,但您必须在
if
之外调用它。 :
errreturn() {
declare -g ERRRETURN
local flags=$(set +o)
set +e
(
set -e
"$@"
)
ERRRETURN=$?
eval "$flags"
}
....
echo ""
else
errreturn "$test"
if (( ERRRETURN == 0 )); then
echo "$test: pass"
else
echo "$test: fail"
fi
fi
关于bash - 如何使 errexit 行为在 bash 函数中起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70987732/