Linux Bash陷阱:如何获取非零状态子流程的行号
对于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
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
或其他类似控件来避免错误<