我试图编写一个在后台空闲等待信号的 shell 脚本。由于脚本不接受用户输入,我想read
在等待时无限期地阻止脚本。
在 bash 中,以下代码似乎按预期工作,每次收到 SIGUSR1 时都会输出“信号”:
#!/bin/bash
trap "echo signal" SIGUSR1
read
$ ./test
signal
signal
...
但是,如果我使用 BusyBox ash 运行#!/bin/sh
,发送 SIGUSR1 也会导致程序终止:
#!/bin/sh
trap "echo signal" SIGUSR1
read
$ ./test
signal
$
不应该read
只在从标准输入读取 EOF 或 IFS 后返回吗?在这种情况下,这并没有发生,那么是什么导致了它的返回呢?
答案1
Dash 和 BusyBox 符合 POSIX 规范,使信号read
立即退出。 Bash 不会,除非在POSIX模式。该部分关于信号状态
当 shell 等待实用程序执行前台命令完成时接收到已设置陷阱的信号时,与该信号关联的陷阱只有在前台命令完成后才会执行。
read
不是一个特殊内置,因此它是一个“作为前台命令执行的实用程序”,即使它碰巧在同一进程内运行。这排除了运行陷阱并继续执行 的 bash 行为read
。
该部分关于执行环境阐明了运行实用程序时信号如何工作(再次包括非特殊内置函数):
如果实用程序是 shell 脚本,则 shell 捕获的陷阱应设置为默认值,shell 忽略的陷阱应设置为实用程序忽略;如果实用程序不是 shell 脚本,则陷阱操作(默认或忽略)应映射到实用程序的适当信号处理操作
内置脚本不是 shell 脚本,但可以说它仍然作为执行脚本的 shell 的一部分运行,因此可以应用第一个子句。但这两种行为都不允许实用程序运行父级的陷阱。唯一可能的行为是read
忽略信号、阻止它或让它立即返回。的描述read
没有提到信号,因此不允许更改标准信号的掩码,这排除了忽略。read
可以说可以阻止信号直到完成(程序可能出于内部目的暂时阻止信号)——但这会与read
在等待输入时不阻止 SIGINT 的期望相冲突(奥卡姆剃刀说,这样做是没有意义的)仅对 SIGINT 有此行为)。因此,read
对 SIGUSR1 (这就是 mksh 的行为方式)不执行任何操作可能在技术上是合规的,但我认为这不是合理的行为。
Dash 和 BusyBox 不完全符合 POSIX 标准。如果被信号中断,则两者(从 Ubuntu 22.04 版本开始)都设置$?
为 1,这与read
退出状态。
由于收到信号而终止的命令的退出状态应报告为大于 128。
(实际上,这是 128 + 信号值,但 ATT ksh 除外,它是 256 + 信号值。)