9

When I run sleep manually, and then kill -INT it, sleep exits immediately. For example:

$  /bin/sleep  60  &
[1] 4002356
$  kill -INT 4002356
[1]+  Interrupt               /bin/sleep 60
$  ps -C sleep
    PID TTY          TIME CMD
$  

However, when I do the same thing in a shell script, sleep ignores the SIGINT. For example:

set  -o xtrace

echo
/bin/sleep  10  &
child="$!"
/bin/sleep  0.1
ps -C sleep
kill  -TERM  "$child"    #  SIGTERM                                         
/bin/sleep  0.1
ps -C sleep
wait  "$child"    #  will return immediately                                

echo
/bin/sleep  10  &
child="$!"
/bin/sleep  0.1
ps -C sleep
kill  -INT  "$child"    #  SIGINT
/bin/sleep  0.1
ps -C sleep
wait  "$child"    #  will wait for 9.8 seconds                              

Why/how does sleep ignore SIGINT when I run sleep inside a shell script?

I get the same behavior with dash and bash. My kernel is Linux 5.4.0.

4
  • 1
    Because of the "&" it is not running "in" the script, but in background, and that may be the reason. Commented May 31, 2021 at 6:30
  • 1
    Observations: (1) set +m seems very relevant. Try with set -m and compare. (2) In my Debian the phenomenon does not occur in posh; this shell does not support set -m/set +m. Commented May 31, 2021 at 6:34
  • I confirm that with dash -m, sleep exits upon receiving SIGINT, as expected. I'm reading your answer now, and pondering how a child process can inherit a signal handler across an exec(). I was not aware that was possible.
    – mpb
    Commented May 31, 2021 at 6:52
  • @mpb A signal handler doesn't persist across exec(), but the ignored/not-ignored status does. More precisely, if a signal has a handler, exec() resets it to the default action, which is to kill the process for many but not all signals (e.g. SIGTSTP, SIGCONT). Commented May 31, 2021 at 15:46

1 Answer 1

10

In /bin/sleep 10 & the terminating & makes the shell run sleep asynchronously. Job control is by default disabled in a script. In Bash the following applies [emphasis mine]:

Non-builtin commands started by Bash have signal handlers set to the values inherited by the shell from its parent. When job control is not in effect, asynchronous commands ignore SIGINT and SIGQUIT in addition to these inherited handlers.

Source: Bash Reference Manual.

And relatedly, SIG_IGN persists across a call to execv(), with the possible exception of SIGCHLD.

Signals set to the default action (SIG_DFL) in the calling process image shall be set to the default action in the new process image. Except for SIGCHLD, signals set to be ignored (SIG_IGN) by the calling process image shall be set to be ignored by the new process image. Signals set to be caught by the calling process image shall be set to the default action in the new process image (see <signal.h>).

Source: The Open Group Base Specifications Issue 7, 2018 edition.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .