5

I was playing around with using trap inside a function because of this question, and came up with this secondary question. Given the following code:

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

If I run d, the trap causes the shell to return from the function without printing 'hi'. So far so good. But if I run it a second time, I get a message from the shell:

-bash: return: can only `return' from a function or sourced script

At first, I assumed this meant the ERR sig was happening twice: Once when false gave a nonzero exit status (inside the function) and again when the function itself returned with a nonzero exit status (outside the function). But that hypothesis doesn't hold up against this test:

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

If I run the above, no matter how often I run it, I no longer get the can only return from a function or sourced script warning from bash. Why does the shell treat a compound command different from a simple command in a trap arg?

My goal was to maintain the actual exit status of the command that caused the function to exit, but I think whatever is causing the above behavior also makes capturing the exit status complicated:

f() {
    trap '
        local s=$?
        echo $s
        return $s' ERR
    false
    echo hi
}

bash> f; echo $?
1
0

Wat? Can someone please explain why $s expands to two different values here and, if it turns out to be the same cause, the above differentiation between return and echo +; return?

4

1 回答 1

3

您的第一个结论是正确的: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'函数。我观察到了同样的行为(我很惊讶)。原因不是“$s”的错误扩展。如果你尝试硬编码一个值,你应该观察到当它被陷阱处理程序执行时,给'return'语句的参数被忽略了。

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

顺便说一句,在我看来,trap 并不是一个很好的用途。我们可以通过创建子外壳来避免陷阱的副作用。在这种情况下,我们避免了父 shell 中的错误,并保留了内部函数的退出代码:

g() (
    trap 'return' ERR
    false
    echo hi
)
于 2014-05-24T20:29:28.143 回答