Linux Bash陷阱:如何获取非零状态子流程的行号

Linux Bash陷阱:如何获取非零状态子流程的行号,linux,bash,shell,centos7,bash-trap,Linux,Bash,Shell,Centos7,Bash Trap,对于Bash程序: 1 #!/bin/bash 2 3 trapinfo() 4 { 5 echo "=== Trap Info: Status=$? LINENO=$@ A=$A" 6 } 7 8 main() 9 { 10 trap 'trapinfo $LINENO -- ${BASH_LINENO[*]}' ERR 11 12 set -e 13 set -E 14 set -o errtrace 15

对于Bash程序:

 1  #!/bin/bash
 2  
 3  trapinfo()
 4  {
 5     echo "=== Trap Info: Status=$? LINENO=$@ A=$A"
 6  }
 7  
 8  main()
 9  {
10     trap 'trapinfo $LINENO -- ${BASH_LINENO[*]}' ERR
11  
12     set -e
13     set -E
14     set -o errtrace
15     shopt -s extdebug
16  
17     local -g A=1
18  
19     # false        # If uncommented, LINENO would be 19
20     (exit 73)      # LINENO is 9. How can I get 20 instead?
21  
22     A=2
23  }
24  
25  main
对于输出:

=== Trap Info: Status=73 LINENO=9 -- 25 0 A=1
我正在寻找一种方法,使退出状态为非零的子shell被
trap
捕获,并显示失败子shell的行号。在上面的示例中,我将查找第20行作为结果。我注意到,如果错误不在子shell中,我会得到想要的行号(请参见上面的
false

我尝试将陷阱移动到子shell之前,以检查行号
9
是否实际连接到陷阱调用,但得到了相同的结果。我还尝试将
集合
shopt
条目也放入子shell——同样,行为没有改变

环境:

  • bash-4.2.46-21.el7_3.x86_64:这是一项要求,但不要求符合POSIX。我还对以后的Bash版本(4.2+)感兴趣
  • CentOS 7+:虽然我主要对CentOS感兴趣,但对于部署在Ubuntu 16.04+和CentOS 6上的Bash脚本,我最终还是需要这个
是否可以获取返回非零状态的子流程的行号?如果不可能,是否有相关文件?如果存在一个解决方案,它应该可以很好地扩展,而不会在代码中进行不必要的修饰

#!/bin/bash

trapinfo()
{
   echo "=== Trap Info: Status=$? LINENO=$@ A=$A"
}

main()
{
   trap 'trapinfo $LINENO $SAVE_IT -- ${BASH_LINENO[*]}' ERR

   set -e
   set -E
   set -o errtrace
   shopt -s extdebug

   local -g A=1

   # false        # If uncommented, LINENO would be 19
   SAVE_IT=$LINENO && (exit 73)     # LINENO is magic, but a custom variable isn’t

   A=2
}

main

也许我遗漏了什么,但也许这对你有用…

我向Bash电子邮件小组寻求帮助。Eduardo Bustamante提供了以下两个代码块,以指出Bash中可能存在的错误,这是这里困难的根源

首先,更简单的问题演示:

     1  #!/bin/bash
     2  shopt -s extdebug
     3  main() {
     4  trap 'echo $LINENO' ERR
     5  (exit 17)
     6  }
     7  main
上面的输出为
3

下一步,考虑更改<代码> $(…)>代码> <代码>……'/COD>:

     1  #!/bin/bash
     2  shopt -s extdebug
     3  main() {
     4  trap 'echo $LINENO' ERR
     5  `exit 17`
     6  }
     7  main
上面的输出为
5

现在,我做了我自己的测试:

     1  #!/bin/bash
     2  shopt -s extdebug
     3  main() {
     4  trap 'echo $LINENO' ERR
     5  $(exit 17)
     6  }
     7  main
这也具有所需的
5
输出

因此,这个问题的解决方案似乎是,这是Bash中的一个bug,解决方法是使用它,而不仅仅是子shell
()


再次感谢爱德华多·布斯塔曼特的洞察力。我会等几天,看看他是否会在这里发布一个解决方案来接受他的回答;否则,我会将此标记为已接受的答案,并向他表示感谢。

此解决方案无法很好地扩展。我提供的示例对于演示这个问题来说是最小的。我的示例也不涉及子流程、多行子流程等的嵌套。在一段复杂的脚本中,我不想在可能返回非零状态的每一行前面都添加这个。我很感激你的回答,但这对我来说并不实际。你的陷阱在上层。子shell知道它在第20行:尝试执行
(eval'echo${LINENO});退出73)
而不是简单的
(退出73)
;如果你不在适当的时候用单引号和
eval
来保护它,你可能会错过这个事实。但是,当执行陷阱时,此信息已丢失。要么你有子shell继承的陷阱(我不知道怎么做),要么你找到一种方法让子shell放弃它:一旦子shell退出,调用方就会忘记它在哪里调用它……这有同样的问题。。。它需要代码的修饰。假设在陷阱之后的
main
内部,我调用函数
f
,该函数在具有非零返回状态的子shell中执行代码。代码的任何修饰本质上都是不可伸缩的。我需要一个仅限于调用
trap
命令本身的解决方案。在这方面,
trap
命令可以具有任何复杂的参数,甚至可以调用其他函数。我希望这里有个解决办法。再次感谢您的反馈。我们运气不好:。。。这意味着您必须为每个子shell显式地设置它们(不可伸缩),或者您应该定义一个传递信息的协议……POSIX合规性不是必需的。实际上,我已经有了Bash代码,它可以处理子shell的多个嵌套级别,并按照需要执行陷阱。这就是为什么我特别提到我不需要POSIX合规性。背景:我正在用变量持久性在Bash中实现try/catch。try块位于子shell中。这一切都运行得很好,包括变量持久性,但我似乎只能知道try块的开始行和结束行。虽然我可以接受这个限制,但我希望有所改进。在第一种形式中,您甚至没有使用
$(…)
,而是使用了一个普通的子shell。用
$(…)
你会得到与反勾号相同的结果。@gniourf_gniourf是的,我自己也做了同样的测试,看到了和你一样的东西。我已经更新了解决方案!是的,我错了。我向电子邮件线程发送了一个更正:我认为这是一个错误,因为
[…]
(…)
有相同的问题(在函数内部执行时,两者都会报告错误的行号,并触发错误)。尝试使用
[[a=b]]
((0))
。两者都将触发错误陷阱,且LINENO值不正确。至少Bash手册页中记录的异常(
if
elif
&&
|
)按预期工作。我很高兴你提到了
[…]
(…)
块(单独),因为我经常使用成语
[…]&&
而不是
if[…];然后,;fi
块。是否有任何方法可以防止
[…]
(…)
代码块(单独)的
错误陷阱?如果没有,这似乎告诉我,我必须改变我的做法,使用
If
或其他类似控件来避免
错误<