Dash and BusyBox comply with the POSIX specification by making the signal quit read
immediately. Bash doesn't, unless invoked in POSIX mode. The section on signals states
When a signal for which a trap has been set is received while the shell is waiting for the completion of a utility executing a foreground command, the trap associated with that signal shall not be executed until after the foreground command has completed.
read
is not a special built-in, so it is a “utility executing as a foreground command”, even if it happens to run inside the same process. This rules out the bash behavior of running the trap and continuing the execution of read
.
The section on the execution environment clarifies how signals work when running a utility (once again, including non-special built-ins):
If the utility is a shell script, traps caught by the shell shall be set to the default values and traps ignored by the shell shall be set to be ignored by the utility; if the utility is not a shell script, the trap actions (default or ignore) shall be mapped into the appropriate signal handling actions for the utility
A built-in is not a shell script, but arguably it's still running as part of a shell that's executing a script, so the first clause could apply. But neither behaviors allow the utility to run the parent's trap. The only possible behaviors are for read
to ignore the signal, block it, or let it return immediately. The description of read
doesn't mention signals, so it isn't allowed to change the mask of a standard signal, which rules out ignoring. read
could arguably block the signal until it finishes (programs may temporarily block signals for internal purposes) — but this would conflict with the expectation that read
does not block a SIGINT while it's waiting for input (with Occam's razor saying that it wouldn't make sense to only have this behavior for SIGINT). So having read
do nothing upon SIGUSR1 (which is how mksh behaves) might be technically compliant, but I don't find this to be reasonable behavior.
Dash and BusyBox aren't fully POSIX-compliant. Both (as of the versions in Ubuntu 22.04) set $?
to 1 if read
is interrupted by a signal, which contradicts the requirement on exit status.
The exit status of a command that terminated because it received a signal shall be reported as greater than 128.
(In practice that's 128 + signal value, except ATT ksh where it's 256 + signal value.)