Errtrace 和本地

imp*_*dge 4 bash function trap error-handling

我正在使用以下脚本:

#!/bin/bash -Eu

trap 'echo Hi' ERR

exit_failure() {
  echo "Hello, World!"
  return 1
}

sub_failure() {
  res=$(exit_failure)
}

sub_failure
Run Code Online (Sandbox Code Playgroud)

其结果如下:

Hi
Hi
Run Code Online (Sandbox Code Playgroud)

但是,如果我更改sub_failure()为以下内容:

Hi
Hi
Run Code Online (Sandbox Code Playgroud)

我没有得到任何输出;ERR不再被困了吗?为什么信号被隐藏了?ERR如果我想使用局部变量,如何捕获?我知道我可以做到local res; res=$(exit_failure),但为什么我必须将两者分开?

mar*_*iux 6

这不是一个错误。这实际上是定义的行为。

使用时bash -Eux您可以看到会发生什么。(-Eu来自你的shebang + -x

+ trap 'echo Hi' ERR
+ sub_failure
++ exit_failure
++ echo 'Hello, World!'
++ return 1
+++ echo Hi
+ res='Hello, World!
Hi'
++ echo Hi
Hi
++ echo Hi
Hi
Run Code Online (Sandbox Code Playgroud)

当进行命令替换时,trap由于-E切换而被继承。return 1因此,由您的函数触发的继承陷阱中的“Hi”exit_failure()将成为存储在 中的值的一部分ret。(使用 执行变体时也是如此local

此外,res=...表达式返回1(错误)并触发你的陷阱(在你的sub_failure()函数内部)。

由于res=...return1和函数的结果是函数中最后一个命令的结果,因此结果sub_failure()也是(错误),并且在主 shell 中执行1后会再次触发陷阱。sub_failure因此,您会得到 2 个可见的“Hi”:一个 forres=....和一个 for sub_failure,以及存储在 中的隐藏“Hi” $res

现在来说说local变体:

+ trap 'echo Hi' ERR
+ sub_failure
++ exit_failure
++ echo 'Hello, World!'
++ return 1
+++ echo Hi
+ local 'res=Hello, World!
Hi'
Run Code Online (Sandbox Code Playgroud)

根据定义,在函数中使用时local始终返回。0导致您local res=...评估为0(成功),同时仍然将隐藏的“Hi”存储在$res. 因为res=..评估结果0 sub_failure也返回0。所以这一次你获得了一次“隐藏”失败和两次成功。

希望这会有所帮助,即使这个线程很旧;)

也应该清楚为什么要local res=...分裂

local res
res=....
Run Code Online (Sandbox Code Playgroud)

是否恢复第一个变体的行为..?;)

  • +1,即使这个线程很旧,分析得很好 (2认同)