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

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

我试图编写一个在后台空闲等待信号的 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 + 信号值。)

相关内容