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