【问题标题】:Identifying received signal name in Bash在 Bash 中识别接收到的信号名称
【发布时间】:2012-02-13 06:59:22
【问题描述】:

收到信号后,我可以使用trap 执行一些命令。示例:

trap 'echo hello world' 1 2

如果接收到任何指定的信号,则会显示“hello world”。

但是如何打印/识别接收到的信号名称?

【问题讨论】:

    标签: linux bash shell


    【解决方案1】:
    for s in {1..64} ;do trap "echo trap $s" $s ;done
    

    或者没有 bash-isms

    s=1 ;while [ $s -le 64 ] ;do trap "echo trap $s" $s ;s=$((s+1)) ;done
    

    设置 64 个单独的陷阱,每个可能的信号一个。

    【讨论】:

      【解决方案2】:

      参考上面$?的解决方案:$?会反映最后执行命令的退出码。考虑一下:

      #!/bin/bash
      trap 'echo CODE: $?; exit 1' 1 2 3 15
      sleep 3600
      

      如果你运行它并按下 Ctrl-C,它会打印出CODE: 130。这是因为 sleep 可执行文件被 SIGINT 中断并以该代码退出。

      比较一下:

      #!/bin/bash
      trap 'echo CODE: $?; exit 1' 1 2 3 15
      read X
      

      如果你运行它并按下 Ctrl-C,它会打印出CODE: 0,大概是因为read 命令是内置的并且退出代码规则不同(如果你要中断,也会发生同样的情况while : ; do : ; done)。

      所以,$? 仅在中断外部命令时告诉您信号,并且如果该特定程序没有捕获信号并使用自己的退出代码退出。以防万一是上面的 bash 脚本:收到 SIGINT 后,它将以代码 1 退出,而不是 130

      【讨论】:

      • 感谢您报告此问题,我已经更新了我的答案。将read X 更改为(read X) 可以作为一种解决方法。
      【解决方案3】:

      (如果您只有信号的编号并且想要名称,kill -l $SIGNAL_NUM 会打印信号的名称;您可以通过在对 trap 的调用中使用信号名称而不是数字来避免这种情况,如下所示。 )

      This answer 表示无法访问信号名称,但如果您对捕获的每个信号都有单独的函数,那么您已经知道信号名称:

      trap 'echo trapped the HUP signal' HUP
      trap 'echo different trap for the INT signal' INT
      

      在许多情况下,这可能就足够了,但another answer on that same question 使用该事实提供了一种变通方法来伪造您想要的行为。它接受一个函数和一个信号列表,并为使用信号名称调用的函数上的每个信号设置一个单独的陷阱,因此在内部它实际上是每个信号的单独函数,但它看起来像单个函数上的单个陷阱,它获取信号名称作为参数:

      代码:

      #!/bin/bash
      
      trap_with_arg() {
          func="$1" ; shift
          for sig ; do
              trap "$func $sig" "$sig"
          done
      }
      
      func_trap() {
          echo "Trapped: $1"
      }
      
      trap_with_arg func_trap INT TERM EXIT
      
      echo "Send signals to PID $$ and type [enter] when done."
      read # Wait so the script doesn't exit.
      

      如果我运行它,那么我可以向进程发送信号并得到类似的输出

      Trapped: INT
      Trapped: TERM
      Trapped: EXIT
      

      【讨论】:

      • 谢谢。但这对我不起作用。该函数 trap_with_arg() 将阻塞,直到收到信号。我希望在收到信号时显示信号名称 - 在执行脚本时。就我而言,我不是在等待信号。每当它到达时,都需要打印。
      • trap_with_arg() 不会阻止。它循环输入,设置陷阱处理程序,然后返回。
      • Lunar Mushrooms,它可能会被阻止,因为当你的脚本中的命令正在运行时,陷阱没有被处理。如果您在脚本位于sleep 中间时发出信号,则在sleep 完成之前它不会执行您的陷阱。它不是异步的。陷阱将捕获脚本中命令之间的信号。
      • 你可能想使用sleep 1000 & pid=$! ; echo "waiting for ${pid}" ; wait $pid之类的东西而不是read,这样更清楚发生了什么
      • @Dirk 啊,我看到了你所做的区分。感谢您的澄清。我已经扩展了我的答案,以便在完全通用的解决方法之前给出一个中间解释。
      【解决方案4】:

      一个简单的方法:

      _handler() {
         signal=$1
         echo signal was $signal
       }
      
       trap '_handler SIGTERM' SIGTERM
       trap '_handler SIGINT'  SIGINT
      

      【讨论】:

      • 与接受的答案相同,但此答案用于单次通话。使用循环进行多次调用。
      【解决方案5】:

      在陷阱内(通过信号触发时),$?变量最初设置为信号编号加 128,因此您可以通过将陷阱操作的第一条语句设置为类似的内容来将信号编号分配给变量

      sig=$(($? - 128))
      

      然后您可以使用 kill 命令获取信号的名称

      kill -l $sig
      

      更新:如 cmets 中所述,这不适用于某些内置 shell 命令。让这些设置陷阱的 $?变量,它们可以在子shell中运行,例如

      (read)
      

      而不是

      read
      

      【讨论】:

      • 那会很好,但它不适用于带有 Bash 4.3.30 的 Linux 3.10:$? 在信号处理程序的开头为 0,即使它已被 @ 调用987654327@信号。
      • 这似乎是最好的答案,因为它更直接。
      • (删除了这条评论,因为我无法在cmets中添加代码块)
      • 不工作,解释见this answer
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-05
      • 1970-01-01
      • 1970-01-01
      • 2022-11-10
      • 1970-01-01
      相关资源
      最近更新 更多