bash - 为什么带有“and”运算符(“&&”)的链式命令不会在启用“errexit”的非零结果上停止?
问题描述
我bash v4.4.12
在 Debian 上进行了默认设置:
$ 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
一切都是对的。行“回声'之后';” 没有打电话。
$ 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
. 仔细阅读为什么 set -e(或 set -o errexit,或陷阱 ERR)没有达到我的预期?
来自GNU bash文档set -e
while
如果失败的命令是紧跟在oruntil
关键字之后的命令列表的一部分、if 语句中的测试的一部分、在&&
or||
列表中执行的任何命令的一部分,则 shell 不会退出,但最后一个&&
or之后的命令除外||
你认为这段代码会发生什么?
#!/usr/bin/env bash
set -e
test -d nosuchdir && echo no dir
echo survived
自己运行它并观察为什么对使用orset -e
运行的命令没有影响。您报告的第三起案件也发生了同样的情况。即使该函数返回一个非零退出代码,使用另一个函数作为其中的一部分已强制 shell 忽略选项集。&&
||
test_1
&&
errorexit
最好避免set -e
并使用您自己添加的错误检查。在这种情况下,在 if 条件中使用函数的返回码并对结果取反
if ! test_1; then
success_1
fi
还阅读了 Bash 脚本中的引发错误,其中有一些关于如何在 shell 中以最佳方式处理错误的很好的答案。
推荐阅读
- javascript - 如何从 modalOverlay 和 closeBtn 中删除重复代码?
- r - 通过列中的最后一个非 NA 值对 df 进行子集
- mongoose - 破解基于 NodeJS 的 API:如何正确使用 Query.skip() 属性?
- java - 在 AWS Elastic Beanstalk 中存储外部 API 密钥
- python - 使用python合并一个json对象数组
- amazon-web-services - 如何触发 lambda 函数事件类型:ObjectCreatedByPut
- mysql - 触发器不产生输出
- docker - Ansible 分子测试错误:无法重新加载 udev 规则:退出状态 1
- swift - Swift 合并接收器数组 AnyPublisher
- azure-devops - 有没有办法创建在新选项卡中打开的 Azure DevOps 拉取请求状态链接?