Terminating background commands vs subshells

Viewed 151

I encountered a behavior I do not understand about how a parent bash script can terminate the execution of its child processes when those child processes are executed in background.

Here are very easy bash scripts to illustrate my mis-understanding.

child.sh which only waits.

#!/bin/bash

while :
do
    sleep 10000
done

version 1 : parent.sh with child scripts directly executed in background

#!/bin/bash

./child.sh &
./child.sh &
wait

If we execute parent.sh and enter Ctrl+C for the parent.sh script to terminate then the 2 child processes are still running after.

$ ./parent.sh
^C
$ ps aux | grep child.sh
946702 pts/5    00:00:00 child.sh
946703 pts/5    00:00:00 child.sh

version 2 : parent-sub-shells.sh with child scripts executed in sub-shells and then in background

#!/bin/bash

(./child.sh) &
(./child.sh) &
wait

If we execute parent-sub-shells.sh and enter Ctrl+C for the parent.sh script to terminate the 2 child processes are correctly killed.

$ ./parent-sub-shells.sh
^C
$ ps | grep child.sh

Why the behavior is different between the 2 versions ? Especially concerning the fact one version is killing the child processes and not the other.

Thanks for your help.

Baptiste

1 Answers

Normally, when a non-interactive shell runs a command in the background (eg ./child.sh &), the shell will start that process as a new process group. This then prevents it from directly accessing (or being affected by) the terminal. In particular, when you hit ^C, it sends a SIGINT to every process in the foreground process group. In your version 1 case, that is just the parent.sh process and so all the child.sh processes remain.

When you run a subshell in the background howeever, it supresses this "creation of a new process group". So the subshell runs in the same process group as the parent and runs child.sh in that same process group. So when you hit ^C, SIGINT is sent to all the processes in the foreground group, which will be all three (five if you count the 'sleep') processes.

Note that the rules for interactive shells are a bit different; they run all children in their own process groups so ^Z works (allowing any 'foreground' process to be put into the background interactively).

Related