在trap/return之后退出状态在哪里



由于这个问题,我在函数中使用trap,并提出了这个次要问题。给定以下代码:

d() {
    trap 'return' ERR
    false
    echo hi
}

如果我运行d, trap使shell从函数返回而不打印'hi'。到目前为止一切顺利。但是如果我第二次运行它,我从shell得到一条消息:

-bash: return:只能从函数或源脚本中'返回'

起初,我认为这意味着ERR信号发生了两次:一次是在false给出非零退出状态时(在函数内部),另一次是在函数本身返回非零退出状态时(在函数外部)。但是这个假设并不能支持这个测试:

e() {
    trap 'echo +;return' ERR
    false
    echo hi
}

如果我运行上面的代码,不管我多频繁地运行它,我都不会再从bash中得到只能从函数或源脚本中获取return的警告。为什么shell处理复合命令不同于trap参数中的简单命令?

我的目标是维护导致函数退出的命令的实际退出状态,但我认为导致上述行为的原因也使捕获退出状态变得复杂:

f() {
    trap '
        local s=$?
        echo $s
        return $s' ERR
    false
    echo hi
}
bash> f; echo $?
1
0

窟?谁能解释一下为什么$s在这里扩展为两个不同的值,如果结果是相同的原因,returnecho +; return之间的上述差异?

你的第一个结论是正确的:ERR信号发生了两次。

在第一次执行'd'时,全局定义一个陷阱。这会影响下一个命令(当前调用d不受影响)。在第二次执行'd'时,您再次定义一个陷阱(不是很有用),调用'false'失败,因此我们执行由陷阱定义的处理程序。然后我们返回父shell,其中'd'也失败了,所以我们再次执行陷阱。

只是一句评论。ERR可以作为'sigspec'参数给出,但ERR不是一个信号;-)来自BASH手册:

If a sigspec is ERR, the command arg is executed whenever a sim‐
ple command has a non-zero exit status, subject to the following
conditions. [...]
These are  the  same  conditions obeyed by the errexit option.

对于函数'e', ERR处理程序执行成功的'echo'命令。这就是为什么'e'函数不会失败,这就是为什么在这种情况下ERR处理程序不会被调用两次。

如果你尝试"e;Echo $?"你会读到"0"

然后我尝试了你的'f'函数。我观察到同样的行为(我很惊讶)。原因是NOT是"$s"的错误展开。如果你试图硬编码一个值,你应该注意到,当陷阱处理程序在执行` return `语句时,给出的参数会被忽略。

我不知道这是正常的行为还是BASH的bug…或者可能是避免解释器中的无限循环的技巧:-)

顺便说一下,在我看来这不是陷阱的好用法。我们可以通过创建子壳来避免陷阱的副作用。在这种情况下,我们避免了父shell中的错误,并保留了内部函数的退出码:
g() (
    trap 'return' ERR
    false
    echo hi
)

最新更新