【发布时间】:2022-02-04 14:09:38
【问题描述】:
如何让-e / errexit 在 bash 函数中工作,以便函数中的第一个失败命令* 导致函数返回错误代码(就像 -e 在顶层工作一样)。
* 不是布尔表达式的一部分,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 的符号链接)
阿尔卑斯山:
-
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 条件失败时不要退出脚本”行为会传播到条件中调用的任何函数中,而不仅仅是应用于最终结果。
【问题讨论】:
-
你没有。封装要求如果
ft,例如,如果它的任何命令失败,它就会被写成这样。它的行为方式不取决于调用者。 -
所以本质上,如果其中的命令失败,除了在每个语句上添加 if/else 或 ||return 之外,没有办法让 bash 函数失败(而不是盲目地向前推进)?跨度>
-
您可以在函数内部本地设置这些标志,至少对于 /bin/sh 有效。
-
@UlrichEckhardt 我用更多的 shell 添加了更多的测试,包括
sh和zsh,以及几个发行版。正如预期的那样,所有情况下的结果都相同。
标签: bash