If I have the following shell script

sleep 30s

And I hit Ctrl+C when the shell script is running, the sleep dies with it.

If I have the following shell script

sleep 30s &

And I hit Ctrl+C when the shell script is running, the sleep continues on, and now has a parent of 1.

Why is that? Doesn't bash propagate Ctrl+C to all the children?

EDIT: If I have the following script

/usr/bin/Xvfb :18.0 -ac -screen 0 1180x980x24 &

where I am spawning a program, this time Ctrl+C on the main process kills the Xvfb process too.

So how/why is Xvfb different from sleep?

In the case of some processes I see that they get reaped by init, in some cases they die. Why does sleep get reaped by init? Why does Xvfb die?


tl;dr; the Xvfb process sets a signal handler for SIGINT and exits when it receives such a signal, but the sleep process doesn't, so it inherits the "ignore" state for SIGINT as it was set by the shell running the script before executing the sleep binary.

When a shell script is run, the job control is turned off, and background processes (the ones started with &) are simply run in the same process group, with SIGINT and SIGQUIT set to SIG_IGN (ignored) and with their stdin redirected from /dev/null.

This is required by the standard:

If job control is disabled (see the description of set -m) when the shell executes an asynchronous list, the commands in the list shall inherit from the shell a signal action of ignored (SIG_IGN) for the SIGINT and SIGQUIT signals.

If the signal disposition is set to SIG_IGN (ignore), that state will be inherited through fork() and execve():

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.

  • 2
    POSIX also forbid shells to allow undoing a SIG_IGN. zsh ignores that (unhelpful) requirement, so in zsh, you can work around it with (trap - INT; sleep 30) &. In other shells, you'd need to use zsh -c 'trap - INT; exec sleep 30' & or use perl or other things that don't have that silly requirement. Oct 30 '18 at 17:10
  • 3
    In recent versions of bash, the problem can be worked around by starting the command as a coproc instead of with &: { coproc sleep 30 < /dev/null >&4 4>&-; } 4>&1 which doesn't seem to ignore SIGINT/SIGQUIT... Oct 30 '18 at 17:18

From the bash man page:

Background processes are those whose process group ID differs from the terminal’s; such processes are immune to keyboard-generated signals

You could handle this in different ways; first, to kill the listed jobs:

trap 'kill $(jobs -p)' INT
sleep 30s &

Alternatively, send a kill to all of the processes in the same process group:

trap 'kill 0' INT
sleep 30s &
  • That doesn't always seem to be the case though. For instance, if I have this in my shell script usr/bin/Xvfb :18.0 -ac -screen 0 1180x980x24 &; wait and I hit Ctrl+C on the parent process, it does call Xvfb too. Why is that? By this logic that process should continue to live, no? Oct 30 '18 at 2:22
  • @HariSundararajan. Are you executing your script remotely through putty or similar?
    – fpmurphy
    Oct 30 '18 at 5:47
  • no, I am sitting in front of the linux box, with the kvm attached to it, and running commands on an xterm. Oct 30 '18 at 6:52

Bash does not forward signals like SIGINT or SIGTERM to processes it is currently waiting on.

One common workaround is to do a trap wait wait as shown in the following example:

    kill -TERM "${child_pid}" > /dev/null 2>&1

trap 'int_handler' INT

echo "Sleeping ... "
sleep 200 &

wait ${child_pid} > /dev/null 2>&1
trap - INT
wait ${child_pid} > /dev/null 2>&1
  • Why does it appear to forward signals when I run certain other commands , such as Xvfb? Oct 30 '18 at 3:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.