为什么发送捕获信号会导致“read”在 POSIX shell 中返回,但在 Bash 中不会返回?

yva*_*vay 5 bash signals shell-script read

我试图编写一个在后台空闲等待信号的 shell 脚本。由于脚本不接受用户输入,我想read在等待时无限期地阻止脚本。

在 bash 中,以下代码似乎按预期工作,每次收到 SIGUSR1 时都会输出“信号”:

#!/bin/bash
trap "echo signal" SIGUSR1
read
Run Code Online (Sandbox Code Playgroud)
$ ./test
signal
signal
...
Run Code Online (Sandbox Code Playgroud)

但是,如果我使用#!/bin/shBusyBox ash 运行,发送 SIGUSR1 也会导致程序终止:

$ ./test
signal
signal
...
Run Code Online (Sandbox Code Playgroud)
$ ./test
signal
$
Run Code Online (Sandbox Code Playgroud)

不应该read只在从标准输入读取 EOF 或 IFS 后返回吗?在这种情况下,这并没有发生,那么是什么导致了它的返回呢?

Gil*_*il' 10

Dash 和 BusyBox 符合 POSIX 规范,使信号read立即退出。Bash 不会,除非在POSIX 模式下调用。关于信号的部分说明

\n
\n

当 shell 等待实用程序执行前台命令完成时接收到已设置陷阱的信号时,与该信号关联的陷阱只有在前台命令完成后才会执行。

\n
\n

read不是特殊的内置命令,因此它是一个作为前台命令执行的 \xe2\x80\x9cility,即使它碰巧在同一进程内运行。这排除了运行陷阱并继续执行 的 bash 行为read

\n

关于执行环境的部分阐明了运行实用程序时信号如何工作(再次包括非特殊内置函数):

\n
\n

如果实用程序是 shell 脚本,则 shell 捕获的陷阱应设置为默认值,shell 忽略的陷阱应设置为实用程序忽略;如果实用程序不是 shell 脚本,则陷阱操作(默认或忽略)应映射到实用程序的适当信号处理操作

\n
\n

内置脚本不是 shell 脚本,但可以说它仍然作为执行脚本的 shell 的一部分运行,因此可以应用第一个子句。但这两种行为都不允许实用程序运行父级的陷阱。唯一可能的行为是read忽略信号、阻止它或让它立即返回。的描述read没有提及信号,因此不允许更改标准信号的掩码,这排除了忽略。read可以说可以阻止信号直到完成(程序可能出于内部目的暂时阻止信号)\xe2\x80\x94 但这会与read在等待输入时不阻止 SIGINT 的期望相冲突(使用 Occam\' s razor 说仅对 SIGINT 具有这种行为是没有意义的)。因此,read对 SIGUSR1(这就是 mksh 的行为方式)不执行任何操作可能在技术上是合规的,但我认为这不是合理的行为。

\n

Dash 和 BusyBox 不完全符合 POSIX 标准。如果被信号中断,则两者(从 Ubuntu 22.04 版本开始)都设置$?为 1 ,这与退出状态的read要求相矛盾。

\n
\n

由于收到信号而终止的命令的退出状态应报告为大于 128。

\n
\n

(实际上,这是 128 + 信号值,但 ATT ksh 除外,它是 256 + 信号值。)

\n