我有一个简单的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
处注释掉#2
会导致执行"Error:"
语句的if
路径。
bash似乎只是忽略了替换内的set -e
,而if
语句只是检查foo()
中最后一条命令的返回代码。
问:是什么原因导致这种奇怪的行为?
A:这就是bash的工作方式,这是正常行为
问:有什么方法可以使bash在命令替换中遵守 set -e
并使其正常工作?
A:您不应为此目的使用set -e
问:如果不使用 set -e
,如何实现此功能(即,仅当执行过程中出现问题时才打印函数的输出)?
A:请参阅已接受的答案和我的“最终想法”部分。
我正在使用:
GNU bash,版本5.0.11(1)-发行版(x86_64-apple-darwin18.6.0)
最终想法/总结(可能对其他人有用)
请注意,使用if ...; then
甚至是&& ... || ...
将禁用大多数“传统” bash错误处理方法(包括set -e
和trap ... ERR
+ {{1} })设计。
如果您想做像我一样的事情,您可能应该手动检查函数内部的返回代码,并手动返回非空退出代码(set -o errtrace
),以避免继续执行错误(无论是否执行此操作,您是否使用dangerous_command || return 1
。
根据回答,set -e
不会在设计的命令替换内传播。
如果您希望实现这样的错误处理逻辑,则可以将set -e
与trap ... ERR
结合使用,这将与在命令替换内部运行的函数一起使用(也就是说,除非将它们放在{{1}中) }语句,它也会禁用set -o errtrace
,因此,在这种情况下,如果您希望在出错时停止函数,则手动返回代码检查是唯一的选择。
如果您考虑一下,那么整个行为都是有意义的:您不会期望脚本以if
语句“保护”的命令终止,因为{{1 }}语句正在检查命令是否成功。
我个人仍然不会完全避免使用trap ... ERR
和if
,因为它们确实有用,但是了解 在不同情况下的表现很重要,因为它们也不是银弹。