我有一个简单的shell脚本,其中包含以下内容:
#!/usr/bin/env bash set -eu set -o pipefail
我还具有以下功能:
foo() { printf "Foo working... " echo "Failed!" false # point of interest #1 true # point of interest #2 }
执行foo()
作为常规命令按预期工作:脚本退出时#1
,由于返回码false
是非零和我们使用set -e
。
我的目标是将函数的输出捕获到foo()
变量中,并仅在执行时出现错误时打印它foo()
。这是我想出的:
printf "Doing something that could fail... " if a="$(foo 2>&1)"; then echo "Success!" else code=$? echo "Error:" printf "${a}" exit $code fi
该脚本不会在处退出,#1
并且会执行"Success!"
该if
语句的路径。注释掉true
at将#2
导致执行语句的"Error:"
路径if
。
看来bash只是忽略set -e
了替换内部,而该if
语句只是检查中最后一条命令的返回码foo()
。
问:什么原因导致这种奇怪的行为?
答:这就是bash的工作方式,这是正常现象
问:有什么方法可以使bash set -e
在命令替换中得到尊重并使其正常工作?
答:您不应set -e
为此目的使用
问:如果不set -e
使用该功能,您将如何实现(即,仅当执行该功能时出现问题时才输出该功能的输出)?
答:请参阅已接受的答案和我的“最终决定”部分。
我在用:
GNU bash版本5.0.11(1)-发行版(x86_64-apple-darwin18.6.0)
最终想法/总结(可能对其他人有用):
Beware that using if ...; then
, or even && ... || ...
will disable most kinds of "traditional" bash error handling methods (this includes set -e
and trap ... ERR
+ set -o errtrace
) by design.
If you want to do something like I did, you probably should check the return codes inside your function manually and return a non-null exit code by hand (dangerous_command || return 1
) to avoid continuing execution on errors (you can do this whether you use set -e
or not).
As answered, set -e
does not propagate inside command substitutions by design.
If you wish to implement error handling logic which does, you can use trap ... ERR
in combination with set -o errtrace
, which will work with functions running inside command substitutions (that is unless you put them inside an if
statement, which will disable trap ... ERR
as well, so in this case manual return code checking is your only option if you wish to stop your function on errors).
If you think about it, this whole behaviour kind of makes sense: you wouldn't expect your script to terminate on a command "guarded" by an if
statement, as the whole point of your if
statement is checking whether the command succeeds or not.
Personally I still wouldn't go as far as avoiding set -e
and trap ... ERR
entirely as they can be really useful, but understanding how they behave in different circumstances is important, because they are no silver bullet either.
问:如果不
set -e
使用该功能,您将如何实现(即,仅当执行该功能时出现问题时才输出该功能的输出)?
您可以通过检查函数的返回值来使用这种方式:
#!/usr/bin/env bash foo() { local n=$RANDOM echo "Foo working with random=$n ..." (($n % 2)) } echo "Doing something that could fail..." a="$(foo 2>&1)" code=$? if (($code == 0)); then echo "Success!" else printf '{"ErrorCode": %d, "ErrorMessage": "%s"}\n' $code "$a" exit $code fi
现在将其运行为:
$> ./errScript.sh Doing something that could fail... Success! $> ./errScript.sh Doing something that could fail... {"ErrorCode": 1, "ErrorMessage": "Foo working with random=27662 ..."} $> ./errScript.sh Doing something that could fail... Success! $> ./errScript.sh Doing something that could fail... {"ErrorCode": 1, "ErrorMessage": "Foo working with random=31864 ..."}
如果$RANDOM
为偶数,则该伪函数代码返回失败,如果为$RANDOM
奇数,则返回成功。
您还需要启用set -e
命令替换:
#!/usr/bin/env bash set -eu set -o pipefail foo() { printf "Foo working... " echo "Failed!" false # point of interest #1 true # point of interest #2 } printf "Doing something that could fail... " a="$(set -e; foo)" code=$? if (($code == 0)); then echo "Success!" else echo "Error:" printf "${a}" exit $code fi
然后将其用作:
./errScript.sh; echo $?
Doing something that could fail... 1
但是请注意,set -e
在shell脚本中使用不是理想的选择,并且在许多情况下可能无法退出脚本。
请检查以下重要帖子 set -e