我在 Debian 上设置了默认的 bash v4.4.12
:
$ bash --version
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
我更喜欢在我的脚本中使用这些选项:
set -o pipefail
set -o errexit
set -o nounset
它会在管道命令 (pipefail
)、执行退出 (errexit
) 和验证未设置的变量 (nounset) 时停止脚本
)。
我有测试脚本:
set -o pipefail
set -o errexit
set -o nounset
set -o xtrace
return_1() { return 22; }
test_1 () {
return_1;
echo 'after';
}
success_1() {
echo success
}
if [ "${1:-}" == 1 ]; then
# make "return 1" in the root level
return_1
fi
if [ "${1:-}" == 2 ]; then
# run test_1, we will get "return 1" within a function
test_1
fi
if [ "${1:-}" == 3 ]; then
# run test_1 and success_1 in chain
# success_1 MUST NOT be ran because test_1 makes non-zero status
# in fact, success_1 will be ran =(
test_1 && success_1
fi
测试。
$ bash /test.sh 1; echo "status: ${?}"
+ '[' 1 == 1 ']'
+ return_1
+ return 22
status: 22
按预期工作。
$ bash /test.sh 2; echo "status: ${?}"
+ '[' 2 == 1 ']'
+ '[' 2 == 2 ']'
+ test_1
+ return_1
+ return 22
status: 22
一切正常。 “echo ‘之后’;”这一行没打电话。
$ bash /test.sh 3; echo "status: ${?}"
+ '[' 3 == 1 ']'
+ '[' 3 == 2 ']'
+ '[' 3 == 3 ']'
+ test_1
+ return_1
+ return 22
+ echo after
after
+ success_1
+ echo success
success
status: 0
完全不对。 :( 1. 行“echo 'after';” 已调用。 2. 函数“success_1”也已调用。
真的,在这种情况下发生了什么?
UPD 手册引用。
最佳答案
您已陷入使用 set -e
的经典陷阱。请仔细阅读 Why doesn't set -e (or set -o errexit, or trap ERR) do what I expected?
来自GNU bash documentation对于 set -e
The shell does not exit if the command that fails is part of the command list immediately following a
while
oruntil
keyword, part of the test in an if statement, part of any command executed in a&&
or||
list except the command following the final&&
or||
您认为这段代码会发生什么?
#!/usr/bin/env bash
set -e
test -d nosuchdir && echo no dir
echo survived
自己运行它并观察为什么 set -e
对使用 &&
或 ||
运行的命令没有影响。您报告的第 3 个rd 案例也是如此。即使函数 test_1
返回非零退出代码,使用另一个函数作为 &&
的一部分已强制 shell 忽略 errorexit
选项集。
最好避免 set -e
并使用您自己添加的错误检查。在这种情况下,在 if 条件中使用函数的返回码并对结果求反
if ! test_1; then
success_1
fi
同时通读 Raise error in a Bash script其中有一些关于如何在 shell 中以最佳方式处理错误的书面答案。
关于bash - 为什么使用 "and"运算符 ("&&"的链式命令)不会在启用 "errexit"的非零结果上停止?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51536657/