bash - 为什么使用 "and"运算符 ("&&"的链式命令)不会在启用 "errexit"的非零结果上停止?

标签 bash error-handling

我在 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 手册引用。

http://man7.org/linux/man-pages/man1/bash.1.html

最佳答案

您已陷入使用 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 or until 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/

相关文章:

linux - Bash - 如何在这种情况下调用函数

python - 从 bash 脚本调用时获取 Python 命令行输出

c - C 中的断言和错误检查

vb6 - 你为什么要使用 "On Error Goto 0"?

linux - getopts 用于标志和选项解析

bash - 通过 bash 从文件中检索电子邮件主题

bash - 在插入模式下保留 vim 拆分窗口

php - 引用 - 这个错误在 PHP 中意味着什么?

C# 在 MVC 中处理用户交互

python - 在 python 装饰器中引发异常是一个好的模式吗?