与ERR和EXIT陷阱一起使用set -e
(errexit
),set -u
(nounset
)时,我观察到一些奇怪的行为。它们似乎相关,因此将它们放在一个问题中似乎是合理的。
1)set -u
不触发ERR陷阱
码:
#!/bin/bash trap 'echo "ERR (rc: $?)"' ERR set -u echo ${UNSET_VAR}
- 预期:ERR陷阱被调用,RC!= 0
- 实际:未调用ERR陷阱,RC == 1
- 注意:
set -e
不改变结果
2)set -eu
在EXIT陷阱中使用退出代码是0而不是1
码:
#!/bin/bash trap 'echo "EXIT (rc: $?)"' EXIT set -eu echo ${UNSET_VAR}
- 预期:调用EXIT陷阱,RC == 1
- 实际:调用EXIT陷阱,RC == 0
- 注意:使用时
set +e
,RC ==1。当任何其他命令引发错误时,EXIT陷阱将返回正确的RC。 - 编辑:关于该主题的一篇SO帖子带有一个有趣的注释,表明这可能与所使用的Bash版本有关。使用Bash 4.3.11测试此代码段会导致RC = 1,所以更好。不幸的是,目前无法在所有主机上升级Bash(从3.2.51开始),因此我们必须提出其他解决方案。
谁能解释这些行为中的任何一个?
搜索这些主题不是很成功,考虑到有关Bash设置和陷阱的帖子数量,这令人惊讶。虽然有一个论坛主题,但是结论并不令人满意。
set -e
和set -u
两者都是专门设计来杀死脚本化的shell。在可能触发其应用程序的条件下使用它们将杀死脚本化的Shell。除了不使用它们,而是在代码序列中应用这些条件时进行测试,这无可避免。因此,基本上,您可以编写良好的shell代码,也可以使用set -eu
。
-u
不触发ERR陷阱(这是一个错误,因此不应该触发陷阱)或错误代码为0而不是1。后者似乎是一个错误,已经在更高版本中修复,就是这样。但是,如果您还没有意识到外壳评估(参数扩展)中的错误和命令中的实际错误似乎是两回事,那么第一部分就很难理解。对于解决方案,正如您所建议的那样,我现在正尝试避免-eu
并在必要时手动进行检查。
(set -u; : $UNSET_VAR)
和类似内容。这种东西也可以很好- &&
偶尔会掉很多东西:(set -e; mkdir dir; cd dir; touch dirfile)
,如果您不满意的话。仅仅是这些是受控的上下文-当您将它们设置为全局选项时,您将失去控制并变得受控。不过,通常会有更有效的解决方案。
bash
违反了标准,开始在子外壳中放置陷阱。该陷阱应该与返回时在相同的环境中执行,但是bash
已经有一段时间没有这样做了。