按信号名称查找信号编号

Find signal number by signal name

我要陷阱信号SIGTSTP,就这么简单:

trap "" SIGTSTP

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

trap "" 20

问题:信号编号是 OS 相关的,所以 SIGTSTP 在 Linux 中是 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.*//p'只有returns8,没有18

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

有什么建议吗?

试试这个:

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

现在一切正常。

加埃塔诺

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


在以下情况下效果很好:

  • 支持 -rsed 实现可以启用对 扩展 正则表达式的支持。
  • bash用于执行命令,因为sed命令期望bash[=20=的输出格式] builtin 使用 -l 选项生成。
  • 平台的正则表达式库支持 \b 词边界断言。

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

警告:

  • 如果您指定 (a) 完整 信号名称(例如,SIGUSR1 与仅 SIGUSR),则接受的解决方案只会明确工作(b) 不是 另一个 信号名称的子串(例如,SIGRTMAXSIGRTMAX-1)。

鉴于 扩展的 正则表达式的功能,在 sed -r 支持 \b 的平台上解决这个缺点并不难(这是 (非便携式)公认解决方案的修正版本):

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

注意末尾的alternation (...|...),它规定信号名称后跟白色space或线.
(请注意,同样使用 \bnot 一般工作,因为它会在 - 之前匹配,某些信号名称的有效部分)。

这里是 (也是不可移植的)等同于 BSD 类平台的修改后的接受答案,包括 OSX:

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

请注意,虽然 -E 也启用 扩展的 正则表达式,但支持的特定风格 与 Linux 不同 ] 一:必须用[[:<:]]代替\b,用[[:blank:]]代替\s

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

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

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

但是请注意,该解决方案本身不 POSIX 兼容:

  • trapbash中的builtin,所以它的trap -l输出格式和一样[=188] =] 在 所有 平台上 bash 运行s - 但请注意 bash 本身,而 POSIX-compatible,并非由 POSIX 本身强制执行,并且实现了 POSIX 标准的许多 扩展 - trap -l 是其中之一他们。
    上面的命令依赖于 bashtrap -l 输出格式,如问题所示。

POSIX兼容性说明:

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

  • 然而,bash 也有一个 kill builtin,其 -l 选项产生 trap -l 相同 输出。内置优先于实用程序,因此 bash kill 内置隐藏外部 kill 实用程序。因此,kill -l(和 trap -l)在 bash 中产生的输出偏离了 POSIX 规定的格式,如下所示:

    • 信号名称以它们的编号为前缀,格式为<number>),后跟space
    • 信号名称 具有 SIG 名称前缀。
  • 因此,bashkill -l 输出主动不符合 POSIX 标准,除非您 运行 bash 在 POSIX 模式(例如,通过 运行ning shopt -so posix)。相比之下,kshzshdash 中的 kill 默认符合 POSIX 规定的输出格式 .

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

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

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

bashkshzsh[=92中通过信号名称获取信号编号=],你可以简单地使用:

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

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


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

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

至于反向操作-通过其数字[=获得信号名称 94=] - 使用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本身列出所有信号名称);请参阅 POSIX utility argument syntax.

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

Ubuntu19.10(procps-ng 3.3.15)自带的procps-ng版本支持两种形式(还有 --list 18)。jarno 致敬以求他的帮助。