Bash 在shell脚本中强制使用set-e的惯用方法
让我们假设我们有这个代码script.sh: 输出:Bash 在shell脚本中强制使用set-e的惯用方法,bash,shell,error-handling,Bash,Shell,Error Handling,让我们假设我们有这个代码script.sh: 输出: $ bash myscript.sh [f] Start [f] Fail! I don't want this executed 我知道。。。在不传播set-e的情况下启动一个子shell,所以我的问题是:在没有太多混乱的情况下,如何按照预期运行?我可以看到3种解决方案,我不喜欢也不确定它们是否真的有效:1将set-e添加到f的开头以及应用程序中的所有其他函数。2运行$set-e和&f。3加上……||对每个可能失败的命令返回1。您应该使用
$ bash myscript.sh
[f] Start
[f] Fail! I don't want this executed
我知道。。。在不传播set-e的情况下启动一个子shell,所以我的问题是:在没有太多混乱的情况下,如何按照预期运行?我可以看到3种解决方案,我不喜欢也不确定它们是否真的有效:1将set-e添加到f的开头以及应用程序中的所有其他函数。2运行$set-e和&f。3加上……||对每个可能失败的命令返回1。您应该使用来调用函数,而不是命令替换,以便set-e仍然有效:
mapfile arr < <(f) # call function f using process substitution
out="${arr[*]}" # convert array content into a string
declare -p out # check output
这不是最漂亮的解决方案,但它确实允许您为当前shell以及任何函数和子shell模拟set-e: 如果以后调用此脚本,则输出到stderr的退出代码将为1-请注意,没有打印到stderr>&2的第二个回显,这证明执行false中止了命令替换:
[f] Start
注:
根据设计,set-e/trap-ERR只对不属于条件集的故障做出响应。有关确切的规则,请参见bash 3.x和4.x之间稍有变化的set-search-for-literal set[描述下的man bash
因此,例如,f不会在以下命令中触发陷阱:if!f;then…,f&&echo ok;以下命令会在子shell命令替换$…,但不会在封闭的条件[…]中触发陷阱:[[$f=='foo']]]&&echo ok,因此整个脚本不会中止
要在这种情况下显式退出函数/子shell,请使用类似于| | return 1/| | | | | | | | | | | | | | | | | | | exit 1的方法,或者在条件优先之外单独调用函数/子shell;例如,在[$f=='foo']]情况下:res=='f也将触发当前shell的陷阱
至于陷阱代码可能被多次调用的原因:在本例中,f中的false首先触发陷阱,然后,因为陷阱的出口1退出子shell$f,所以对于运行脚本的当前shell,陷阱再次被触发
非常有趣,我不知道这种方法。不过写起来有点麻烦。我将在f中添加一些回音,使declare-p out显示一些有意义的内容。是的,这是正确的输出,因为在该错误命令之后,函数f退出。有趣的替代选项-set-e确实适用于进程替换中的命令以及o与命令替换相反,尽管失败不会导致整个脚本中止。请注意,进程替换中的命令也在子shell中运行,您可以按如下方式进行验证:f{echo$++v;};v=1;cat-Yes你是对的。因此set-e在进程替换中对subshell有效,但在命令替换中无效。谢谢!我已经用trap进行了测试…呃,但是set-e没有发挥作用。我认为这是一个非常酷的解决方法,你不需要更改代码本身。当从if:if!f;然后echo f调用f时,我遇到了问题失败;fi->[f]失败!我不想要这个executed@tokland:恐怕这是出于设计-请看我的更新。set-E和解释良好的答案++选项3的有趣用法实际上是一个。@chepner:Good read.cmd | | return 1在概念上是明确的,但很麻烦。。。
[f] Start
declare -- out="f:before-false1
f:before-false2
"
#!/bin/bash
# Set up an ERR trap that unconditionally exits with a nonzero exit code.
# Similar to -e, this trap is invoked when a command reports a nonzero
# exit code (outside of a conditional / test).
# Note: This trap may be called *multiple* times.
trap 'exit 1' ERR
# Now ensure that the ERR trap is called not only by the current shell,
# but by any subshells too:
# set -E (set -o errtrace) causes functions and subshells to inherit
# ERR traps.
set -E
f() {
echo "[f] Start" >&2
echo "f:before-false1"
echo "f:before-false2"
false
echo "f:after-false"
echo "[f] Fail! I don't want this executed" >&2
}
out=$(f)
[f] Start