为什么使用“and”运算符(“&&”)的链命令不会在启用“errexit”的情况下停止于非零结果?

Kir*_*rby 2 error-handling bash

bash v4.4.12在 Debian 上进行了默认设置:

$ bash --version
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
Run Code Online (Sandbox Code Playgroud)

我更喜欢在我的脚本中使用这些选项:

set -o pipefail
set -o errexit
set -o nounset
Run Code Online (Sandbox Code Playgroud)

它使管道命令 ( ) 中的非零结果停止脚本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
Run Code Online (Sandbox Code Playgroud)

测试。

$ bash /test.sh 1; echo "status: ${?}"
+ '[' 1 == 1 ']'
+ return_1
+ return 22
status: 22
Run Code Online (Sandbox Code Playgroud)

按预期工作。

$ bash /test.sh 2; echo "status: ${?}"
+ '[' 2 == 1 ']'
+ '[' 2 == 2 ']'
+ test_1
+ return_1
+ return 22
status: 22
Run Code Online (Sandbox Code Playgroud)

一切都是对的。这行“回显‘之后’;” 没打电话。

$ 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
Run Code Online (Sandbox Code Playgroud)

完全不对。:( 1. 行“echo 'after';”已调用。2. 函数“success_1”也已调用。

真的,这个案子到底是怎么回事?

UPD手册参考。

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

Ini*_*ian 6

您已经陷入了使用set -e. 请仔细阅读为什么 set -e (或 set -o errexit 或 trap ERR)没有达到我的预期?

来自GNU bash文档set -e

while如果失败的命令是紧跟在oruntil关键字之后的命令列表的一部分、if 语句中测试的一部分、在&&or||列表中执行的任何命令的一部分(最后一个或后面的命令除外),&&则shell 不会退出。||

您认为这段代码会发生什么?

#!/usr/bin/env bash
set -e
test -d nosuchdir && echo no dir
echo survived
Run Code Online (Sandbox Code Playgroud)

自己运行它并观察为什么对使用或set -e运行的命令没有影响。您报告的第三个案例也发生了同样的情况。即使该函数返回非零退出代码,使用另一个函数作为 的一部分也会强制 shell 忽略该选项集。&&||test_1&&errorexit

最好避免set -e并使用您自己添加的错误检查。在这种情况下,在 if 条件中使用函数的返回码并对结果取反

if ! test_1; then
    success_1
fi
Run Code Online (Sandbox Code Playgroud)

另请阅读Raise error in a Bash script,其中有一些关于如何在 shell 中以最佳方式处理错误的答案。