为什么在没有(!)的函数中禁用bash -e

时间:2011-07-10 08:48:24

标签: bash

注意:如果您正在寻找解决方法,因为set -e在函数中不起作用,请转到“set -e” in a function。这个问题是关于为什么它没有按预期工作。

当在GNU bash上运行以下版本时,版本4.1.5(1)-release:

  set -ex

  PS4=' ${FUNCNAME[0]}: $LINENO: '

  function f() {
      set -e
      false
      echo $@
  }

  ! f why does it display ? It should stop because of -e
  f

显示

 : 11: f why does it display '?' It should stop because of -e
 f: 6: set -e
 f: 7: false
 f: 8: echo why does it display '?' It should stop because of -e
why does it display ? It should stop because of -e
 : 12: f
 f: 6: set -e
 f: 7: false

我希望它永远不会超过false,因为-e表示“当命令具有非零退出状态时退出”。我知道-e有一个棘手的行为,如http://mywiki.wooledge.org/BashFAQ/105中所述,但我想了解在这个特定情况下会发生什么。我正在使用-e,它在许多非常简单的场景中证明是最有用的。这种情况有点棘手,但如果可以解释,我可以使用-e代替每行后添加|| exit 1

3 个答案:

答案 0 :(得分:5)

在尝试解决类似问题时,我为set -e阅读了man page

  

-e errexit

     

如果一个简单的命令退出非零,则立即退出    状态,除非失败的命令是until或的一部分    while循环,if语句的一部分,&&||列表的一部分,    或者如果使用!反转命令的返回状态。

注意关于返回状态被倒置的最后一位。我想函数f的逻辑否定必然会影响该函数中set -e的行为。

答案 1 :(得分:2)

以下是trap的{​​{1}}部分(我的重点):

  

如果sigspec是ERR,则只要简单命令具有非零退出状态,就会执行命令arg,具体取决于以下条件。 如果失败的命令是紧跟在while或until关键字之后的命令列表的一部分,if语句中的部分测试,在&&中执行的命令的一部分,则不执行ERR陷阱;或||列表,或者如果命令的返回值被反转!这些是errexit选项遵循的相同条件。

所以man bash有效地禁用! command内的ERR trap。我猜想,如果你在使用command运行退出代码后“明确地”检查退出代码,那么可能不希望命令提前退出。看起来很糟糕的设计,因为它可以用来在任何地方有效地禁用安全措施。

答案 2 :(得分:1)

我无法准确回答为什么,但我找到了这个代码段here

  

在一个稍微相关的说明中,默认情况下,bash会获取管道中最后一项的错误状态,这可能不是您想要的。例如,false | true将被视为已成功。如果您希望失败,那么您可以使用set -o pipefail使其失败。

通过更新您的脚本:

function f() {
    echo $SHELLOPTS
    set -e
    set -o pipefail
    false
    echo "$@"
}

它似乎表现得像你期望的那样。

因此,我对“为什么”(可能与你现在相同)的最佳猜测是!导致函数以某种“管道”模式处理。再次......为什么!意味着“管道”,我想我真的不知道。也许一个更好的bash专家可以为我们回答这个部分。