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 & wait
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 & wait
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?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
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.
Method 2
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:
#!/bin/bash trap 'kill $(jobs -p)' INT sleep 30s & wait
Alternatively, send a kill to all of the processes in the same process group:
#!/bin/bash trap 'kill 0' INT sleep 30s & wait
Method 3
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:
int_handler()
{
kill -TERM "${child_pid}" > /dev/null 2>&1
}
trap 'int_handler' INT
echo "Sleeping ... "
sleep 200 &
child_pid=$!
wait ${child_pid} > /dev/null 2>&1
trap - INT
wait ${child_pid} > /dev/null 2>&1
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0