【问题标题】:Bash Trap: How to Get Line Number of a Subprocess with Non-Zero StatusBash Trap:如何获取非零状态的子进程的行号
【发布时间】:2016-12-27 14:23: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 的行号。在上面的示例中,我正在寻找 line 20 作为结果。我注意到,如果错误不在子 shell 中,我会得到想要的行号(参见上面的 false)。

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

环境:

  • bash-4.2.46-21.el7_3.x86_64:这是一项要求,但不要求符合 POSIX。我也对以后的 Bash 版本 (4.2+) 感兴趣。
  • CentOS 7+:虽然主要对 CentOS 感兴趣,但我最终将需要它用于部署在 Ubuntu 16.04+ 和 CentOS 6 上的 Bash 脚本。

是否可以获取返回非零状态的子进程的行号?如果不可能,是否有任何文件可以证明这一点?如果存在解决方案,它应该可以很好地扩展,而无需在代码中进行不必要的修饰。

【问题讨论】:

    标签: linux bash shell centos7 bash-trap


    【解决方案1】:
    #!/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
    

    也许我错过了什么,但也许这对你有用......

    【讨论】:

    • 这个解决方案不能很好地扩展。我提供的示例仅用于演示该问题。我的示例也不处理嵌套子进程、多行子进程等。在一段复杂的脚本中,我不想在可能返回非零状态的每一行前面添加它.我很欣赏这个答案,但这对我来说并不实用。
    • 你有陷阱在上层。子shell 知道它在LINENO 20:尝试执行(eval 'echo ${LINENO}'; exit 73) 而不是简单的(exit 73);如果你没有在适当的时候用单引号和eval 保护它,你可能会错过这个事实。但是,当陷阱被执行时,此信息已丢失。要么你让子shell继承了陷阱(我不知道该怎么做),要么你找到了让子shell传递它的方法:一旦子shell退出,调用者就会忘记它调用它的位置。 .
    • 这有同样的问题......它需要修饰代码。想象一下,在陷阱之后的main 内部,我正在调用函数f,并且该函数在具有非零返回状态的子shell 中执行代码。 任何修饰的代码本质上都是不可扩展的。我需要一个仅限于调用 trap 命令本身的解决方案。在这方面,trap 命令可以有任何复杂的参数,甚至可以调用其他函数。我希望在这里有一个解决方案。再次感谢您的反馈。
    • 我们不走运:traps are never inherited by subshells, as per POSIX standard ... 这意味着您必须为每个子shell(不可扩展)显式设置它们,或者您应该定义一个传递信息的协议...
    • POSIX 合规性不是必需的。实际上,我已经有 Bash 代码可以处理多个嵌套级别的子 shell 并执行想要处理的陷阱。这就是为什么我特别提到我不需要 POSIX 合规性。 背景: 我正在使用可变持久性在 Bash 中实现 try/catch。 try 块位于子shell 中。这一切都很好,包括变量持久性,except 我似乎只能知道 try 块的开始行和结束行。虽然我可以忍受这个限制,但我希望能有所改善。
    【解决方案2】:

    我向 Bash 电子邮件组 help-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的输出。

    接下来,考虑将$(...) 更改为`...`

         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 中的一个错误,解决方法是使用 command substitution 而不仅仅是一个子shell ()

    再次感谢 Eduardo Bustamante 的洞察力。我会等几天看看他是否在这里发布解决方案以接受他的回答;否则,我会将其标记为已接受的答案并感谢他。

    【讨论】:

    • 你甚至没有在第一种形式中使用$(...),你使用了一个普通的子shell。使用$(...) 与使用反引号相同。
    • @gniourf_gniourf 是的,我自己也做了同样的测试,看到的和你一样。我已经更新了解决方案!
    • 是的,我错了。我向电子邮件线程发送了一个更正:lists.gnu.org/archive/html/bug-bash/2016-12/msg00122.html 我认为这是一个错误,因为[[ ... ]](( ... )) 有相同的问题(在函数内部执行时都会报告错误的行号并触发错误) .试试[[ a = b ]](( 0 ))。两者都会使用不正确的 LINENO 值触发 ERR 陷阱。
    • 至少 Bash 手册页中记录的异常(ifelif&&||!)按预期工作。我很高兴您提到了 [[ ... ]](( ... )) 块(孤立地),因为我经常使用成语 [[ ... ]] && <list> 而不是 if [[ ... ]]; then <list>; fi 块。 有什么方法可以防止[[ ... ]](( ... )) 代码块的ERR 陷阱(单独)? 如果没有,这似乎告诉我必须改变我的做法以使用@987654352 @ 或其他类似的控件来避免 ERR 我不想要的陷阱。
    • 感谢所有参与的人。请参阅 lists.gnu.org/archive/html/help-bash/2017-01/msg00003.html 以获取 Chet Ramey 的确认,该确认将在下一个版本的 Bash 中修复。将此标记为解决方案,确认解决方法是使用命令替换。
    猜你喜欢
    • 2020-02-27
    • 2015-03-11
    • 2012-01-17
    • 2020-10-24
    • 1970-01-01
    • 1970-01-01
    • 2017-03-19
    • 1970-01-01
    • 2012-02-25
    相关资源
    最近更新 更多