我们假设我们有这段代码(script.sh
):
#!/bin/bash
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)
输出:
$ 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)将... || return 1
添加到可能失败的每个命令。
答案 0 :(得分:3)
这不是最漂亮的解决方案,但它允许您为当前的shell 以及任何函数和子shell 模拟set -e
:
#!/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)
如果你调用这个脚本输出(到stderr)(之后的退出代码将是1
):
[f] Start
注意:
按照设计,set -e
/ trap ERR
仅响应不属于条件的失败(请参阅man bash
, set
(搜索文字“set [”),确切的规则,在Bash 3.x和4.x之间略有变化。
因此,例如,f
不会在以下命令中触发陷阱:if ! f; then ...
,f && echo ok
;以下内容触发子shell中的陷阱 (命令替换$(...)
,但不包含封闭的条件([[ ... ]]
):[[ $(f) == 'foo' ]] && echo ok
,因此脚本作为一个整体不会中止。
要在这种情况下明确退出函数/子shell ,请使用|| return 1
/ || exit 1
之类的内容,或分别调用函数/ subshell <条件优先的,外;例如,在[[ $(f) == 'foo' ]]
情况下:res=$(f); [[ $res == 'foo' ]]
- res=$(f)
也将触发当前shell的陷阱。
至于为什么可以调用trap
代码多次次:在手头的情况下,false
在 {{ 1}} first 触发陷阱,然后,因为陷阱的f()
退出子shell (exit 1
),陷阱被触发再次 当前 shell(运行脚本的那个)。
答案 1 :(得分:1)
而不是命令替换,您应该使用process substitution来调用您的函数,以便set -e
保持有效:
mapfile arr < <(f) # call function f using process substitution
out="${arr[*]}" # convert array content into a string
declare -p out # check output
<强>输出:强>
[f] Start
declare -- out="f:before-false1
f:before-false2
"