我正在努力解决为什么 errexit 又名
set -e
没有传递到命令替换子 shell(使用 $(my-command)
或反引号调用),而是传递到常规子 shell(使用 (my-command)
调用)。
这个例子:
#!/usr/bin/env bash
set -e
# or:
#set -o errexit
foo() {
echo "foo()" >&2
# Fetch output of actual command, in real life this could be something like
# and fail because you're not authorized or something
#somevar=$(curl --fail --silent "restapi.example.com/get-stuff")
somevar=$(echo foo; exit 42)
echo "foo() after subcommand, exit code: $?" >&2 # this resets $?
echo "foo() last exit code: $?" >&2
}
echo '=== Run in $() ===' >&2
a=$(foo) # runs past this point
echo "code: $?" >&2
echo "=== Run in () ===" >&2
(foo) # fails
echo "code: $?" >&2
echo "=== Run directly ===" >&2
foo # fails
echo "code: $?" >&2
这种行为对我来说有点奇怪,因为这完全没有意义..进一步测试这一点:
#!/usr/bin/env bash
set -e
# or:
#set -o errexit
echo '=== Run 1 ===' >&2
a=$(b=$(exit 42); echo "möp")
echo "code: $?" >&2
# echo '=== Run 2 ===' >&2
# a=$(b=$(exit 43)) # fails without reset of $?
# echo "code: $?" >&2
echo '=== Run 3 ===' >&2
a=$((exit 44); echo "möp")
echo "code: $?" >&2
echo '=== Run 4 ===' >&2
a=$((exit 45)) # works regardless of not resetting $?
echo "code: $?" >&2
# echo '=== Run 5 ===' >&2
# a=$(exit 46; echo "möp") # fails
# echo "code: $?" >&2
# echo '=== Run 6 ===' >&2
# (exit 47; echo "möp") # fails
# echo "code: $?" >&2
# echo '=== Run 7 ===' >&2
# ((exit 48); echo "möp") # fails
# echo "code: $?" >&2
(注释掉的测试将在此时停止脚本)
我想知道为什么它会这样,以及这是否是一个错误。我自己对此有足够的解决方法,并且无论如何,围绕这些命令编写更多的故障保护可能会更聪明(特别是如果这实际上是针对 REST API 的卷曲,其中更多的事情可能会出错),但是为什么 bash 的行为像这样?
测试
您需要
shopt -s inherit_errexit
才能通过命令替换继承选项。请参阅内置 Shopt:
inherit_errexit
If set, command substitution inherits the value of the errexit option,
instead of unsetting it in the subshell environment. This option is
enabled when POSIX mode is enabled.