【问题标题】:Find signal number by signal name按信号名称查找信号编号
【发布时间】:2015-06-05 08:16:22
【问题描述】:

我想捕获信号SIGTSTP,就这么简单:

trap "" SIGTSTP

但是,纯 shell (sh) 不支持信号名称,因此陷阱必须使用信号编号,如下所示:

trap "" 20

问题:信号数取决于操作系统,因此 Linux 中的 SIGTSTP 为 20,但在 AIX 中为 18。

所以为了使其通用,我决定从trap -l 的结果中提取信号编号。原始输入是:

 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
 5) SIGTRAP      6) SIGABRT      7) SIGEMT       8) SIGFPE
 9) SIGKILL     10) SIGBUS      11) SIGSEGV     12) SIGSYS
13) SIGPIPE     14) SIGALRM     15) SIGTERM     16) SIGURG
17) SIGSTOP     18) SIGTSTP     19) SIGCONT     20) SIGCHLD
21) SIGTTIN     22) SIGTTOU     23) SIGIO       24) SIGXCPU
25) SIGXFSZ     27) SIGMSG      28) SIGWINCH    29) SIGPWR
30) SIGUSR1     31) SIGUSR2     32) SIGPROF     33) SIGDANGER
34) SIGVTALRM   35) SIGMIGRATE  36) SIGPRE      37) SIGVIRT
38) SIGALRM1    39) SIGWAITING  50) SIGRTMIN    51) SIGRTMIN+1
52) SIGRTMIN+2  53) SIGRTMIN+3  54) SIGRTMAX-3  55) SIGRTMAX-2
56) SIGRTMAX-1  57) SIGRTMAX    60) SIGKAP      61) SIGRETRACT
62) SIGSOUND    63) SIGSAK   

我不能使用grep,因为我需要的功能--only-matching 并不总是得到支持。 trap -l | grep -oE "[0-9]+\) SIGTSTP" | cut -d')' -f1 运行良好,但仅适用于 Linux。

由于here描述的贪心问题,我也未能使用sed

所以trap -l | sed -nr 's/.*([0-9]+)\) SIGTSTP.*/\1/p' 只返回8,而不是18

我想让提取尽可能通用,所以我不会假设SIGTSTP 是一个两位数的代码,即使实际上它是。

有什么建议吗?

【问题讨论】:

标签: linux shell sed signals aix


【解决方案1】:

要在bashkshzsh按名称获取信号编号,您只需使用:

kill -l TSTP # -> e.g., 18 - case doesn't matter, but do not use the "SIG" prefix

由于这使用了kill内置(相对于外部实用程序版本,/bin/kill),这应该可以在支持这些 shell 的任何平台上工作.


这是一个不依赖于特定 shell 的 POSIX 兼容解决方案

/bin/kill -l | tr -s '[:blank:]' '\n' | 
  awk -v name='TSTP' 'toupper($0) == toupper(name) {print NR}' # don't use "SIG" prefix
  • 外部kill 实用程序/bin/kill-l 选项一起用于列出信号。虽然POSIX spec for kill 规定了一种输出格式,但它并不强制要求以特定顺序列出信号。但是,有意义的顺序是它们的数值,在实践中似乎是这样,这种方法依赖于它。
  • 需要注意的是,/bin/kill - 与 bash、ksh、zsh 和 dash 中的 kill 内置函数不同 - 在 Linux 上不会报告 SIGRTMINSIGRTMAX 的信号。

至于 inverse 操作 - 通过其编号获取信号name - 使用kill -l <number>,例如:

 kill -l 18  # -> e.g., 'TSTP' 

bashkshzshdash 中的 kill 内置函数都支持这一点。

外部/bin/kill 实现在上述语法方面有所不同:

  • macOS, GNU kill:上述形式有效。

  • procps-ng kill,在 18.04 或更低版本的 Ubuntu 上存在,例如:只接受
    kill -l18(或kill --list=18);也就是说,选项参数必须直接附加-l选项,这是符合POSIX的语法,因为信号编号是一个可选选项 -参数(-l 本身列出 all 信号名称);见POSIX utility argument syntax

不幸的是,上面列出的其他实现支持 - 可能不明确 - -l 18 形式。

Ubuntu 19.10 (procps-ng 3.3.15) 附带的 procps-ng 版本支持 both 形式(以及 --list 18)。提示向jarno 寻求帮助。

【讨论】:

    【解决方案2】:

    尝试使用单词边界来限制匹配:

    trap -l | sed -nr 's/.*\b([0-9]+)\) SIGTSTP.*/\1/p'

    这 [对我而言] 为目标字符串和 SIGPIPE 正确测试。

    【讨论】:

    • @mklement0:嗯,这很奇怪——OP 在他的问题中使用了它。
    • 好点。不过,我认为这里需要一个仅支持 POSIX 功能的解决方案。
    • 由于使用-r OP 的一个选项,这很好用,但我建议以下变体以避免歧义(例如,SIGURS,或区分SIGRTMAX-1 from SIGRTMAX): trap -l | sed -nr 's/.*\b([0-9]+)\) SIGTSTP(\s.*|$)/\1/p' 要在 BSD/OSX 上运行,您必须使用 trap -l | sed -nE 's/.*[[:<:]]([0-9]+)\) SIGTSTP([[:blank:]].*|$)/\1/p'
    • @mklement0: 完善的补充
    【解决方案3】:

    试试这个:

    trap -l | sed  -nr 's/^([^0-9]*|.*[ \t])([0-9]+)\) SIGURG.*$/\2/p'
    

    现在一切运行正常。

    加埃塔诺

    【讨论】:

    • 不适用于行首的匹配 -- 尝试 SIGPIPE
    • 正如@jstevenco 指出的那样,这仅适用于两位数的信号代码并且当信号不是行首时。如果 SIGTSTP 是 8 而不是 18 怎么办?
    • 对不起,我纠正了命令的错误,你能不能让我知道谢谢
    • @jstevenco 再次为这个错误道歉
    【解决方案4】:

    更新:我找到了much simpler solution这个对于sed 版本和编写可移植的sed 命令之间的差异,一些人可能仍然感兴趣。


    accepted answer 在以下情况下运行良好:

    • 支持-r 以支持扩展正则表达式的sed 实现可用。
    • bash 用于执行命令,因为 sed 命令需要 bash trap builtin 使用 -l 选项生成的输出格式.
    • 平台的正则表达式库支持\b 用于字边界断言。

    这适用于 OP,但并非所有平台都满足这些要求,尤其是类似 BSD 的平台,包括 OSX。

    警告:

    • 只有当您指定 (a) 完整 信号名称(例如,SIGUSR1 与仅 SIGUSR),即 (b) 不是 em>另一个信号名称(例如,SIGRTMAXSIGRTMAX-1)。

    鉴于 扩展 正则表达式的功能,在支持 sed -r 且支持 \b(这是(非便携式)接受的解决方案):

    trap -l | sed -nr 's/.*\b([0-9]+)\) SIGTSTP(\s.*|$)/\1/p'
    

    注意末尾的 alternation (...|...),它规定信号名称后跟空格或行尾。
    (请注意,同样使用\b 也将正常工作,因为它会在- 之前匹配,这某些信号名称的有效部分。 p>

    这是针对类似 BSD 的平台(包括 OSX)修改后的接受答案的(也是不可移植的)等效项

    trap -l | sed -nE 's/.*[[:<:]]([0-9]+)\) SIGTSTP([[:blank:]].*|$)/\1/p'
    

    请注意,虽然-E 也启用扩展 正则表达式,但支持的特定风格与Linux 不同:必须使用[[:&lt;:]] 而不是\b , 和 [[:blank:]] 而不是 \s

    遗憾的是,POSIX BREs (basic regular expressions) 的限制不允许交替使用,并且使用 sed 可移植地将一个限制为 BRE。 因此,来自 amended 命令的 extended 正则表达式不能直接用符合 POSIX 的 BRE 模拟。 (请注意,GNUsed确实支持 BRE 中的交替,但这是一个不兼容的扩展。)

    这是一个强大、便携的解决方案应该可以在任何与bash 兼容的 POSIX 平台上工作;它采用了一种无法使用正则表达式替代的解决方法:

    trap -l |
     sed -n 's/^/ /; s/$/ /; s/.*[[:blank:]]\([0-9]\{1,\}\)) SIGTSTP[[:blank:]].*/\1/p'
    
    • s/^/ /; s/$/ / 只是在每一行前面加上一个空格字符,从而保证每个信号编号和信号名称都用空格分隔,从而简化了匹配。
    • 请注意,由于必须使用 basic 正则表达式,因此需要特殊字符。例如({ 需要转义
    • POSIX 字符。必须使用诸如 [[:blank:]] 之类的类来代替诸如 \s 之类的快捷方式。
    • 重复符号+ 必须与\{1,\} 模拟。

    但请注意,该解决方案本身不符合 POSIX:

    • trapbash 中的内置,因此它的trap -l 输出格式在bash 运行的所有 平台上相同开启 - 但请注意 bash 本身虽然 POSIX-兼容,但 POSIX 本身并没有强制要求它实现了对 POSIX 标准的许多扩展 - trap -l 是其中之一其中。
      上述命令依赖于bashtrap -l 输出格式,如问题所示。

    POSIX 兼容性说明:

    • POSIX trap builtin 不支持-l,而POSIX kill utility 支持,但它规定了一种不同的格式
      “当指定-l选项时,每个信号的符号名称应按以下格式编写:"%s%c", &lt;signal_name&gt;, &lt;separator&gt;,其中&lt;signal_name&gt;为大写,不带SIG前缀,&lt;separator&gt;应可以是&lt;newline&gt;&lt;space&gt;。对于最后写入的信号,&lt;separator&gt; 应为&lt;newline&gt; - http://man.cx/kill;
      例如:HUP INT QUIT ILL ... 规范并没有强制要求列出信号的顺序,但在实践中,实现似乎按数值升序列出它们。

    • 不过,bash 也有一个kill 内置,其-l 选项会产生与trap -l 相同的输出。内置函数优先于实用程序,因此 bash kill 内置函数会影响外部 kill 实用程序。因此,kill -l(和trap -l)在bash 中产生的输出偏离了POSIX 强制格式,如下所示:

      • 信号名称​​以编号为前缀,格式为&lt;number&gt;),后跟一个空格
      • 信号名称​​确实SIG名称前缀。
    • 因此,bashkill -l 输出主动非 POSIX 兼容,除非您在 POSIX 模式下运行 bash(例如,通过运行 shopt -so posix)。相比之下,kshzshdash DO 中的 kill 内置函数默认情况下符合 POSIX 强制输出格式。

    【讨论】:

    • :D 我不知道kill -l 产生与trap -l 相同的结果。感谢您的努力。如果我的问题是针对 POSIX 兼容的解决方案,我一定会选择您的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多