Reputation: 431
I have a script that launches another script in the background, and then terminates it. I was expecting the child script to be gone, but in the end it still manages to print some output. Here is the example:
in script one.sh:
echo "this is one"
./two.sh &
sleep 1
pid=$!
kill $pid
echo "this was one"
in script two.sh:
echo "this is two"
./three.sh
echo "this was two"
in script three.sh:
echo "this is three"
sleep 5
echo "this was three"
I ran ./one.sh which is supposed to run two.sh in the background, which in turn runs three.sh but not in the background! The output is get is:
this is one
this is two
this is three
this was one
this was three
Shouldn't "this was three" not appear in the output since three.sh was not ran in the background and two.sh was terminated by one.sh? Could you also point me towards any documentation that describes how processes behave when (not) in background and what happens when they are terminated?
Thank you very much for all your help!
Upvotes: 9
Views: 6058
Reputation: 3654
When you start a new process from a bash script this is basically done via fork() .
The new process, referred to as the child, is an exact duplicate of the calling process, referred to as the parent (except for a number of points that can be found in man fork).
If a parent dies the child becomes a child of the init process. Then it is the role of the init process to collect the return code of the child (reaping) after it has exited. So when you kill "two", "three" isn't killed but just gets a different parent. And this is the reason for the trailing three.
The question is discussed from a C-point-of-view here : How to make child process die after parent exits?
Upvotes: 2
Reputation: 4363
The reason this may seem surprising is that one might expect the TERM signal (the default from "kill") to be propagated to child processes, in other words, that the SIGTERM signal (signal #15) received by two.sh would be propagated to three.sh as well. However, this is not actually the case. Killing two.sh simply leaves three.sh to be fostered out to the "init" process (proceess ID 1) as its new parent process, and init will clean up after three.sh when it exits.
The situation gets more complicated with process groups, and the bash documentation talks about how keyboard-generated signals get send to all processes within the foreground process group, often a pipeline being run without an "&" on the end. However, these issues don't apply to the example scripts.
Note: In Unix, you shouldn't use ".sh" extensions on executable scripts. Focus on putting the right "#!/bin/bash" or "#!/bin/sh" on the first line instead. Commands should not expose their implementation language in the command name, lest one have to leave the wrong one on later when the implementation language changes, but other code has come to rely on the original, now incorrect extension.
Upvotes: 0
Reputation: 18550
You're killing the backgrounded process two.sh
, but not two.sh
and its child three.sh
.
This question:
Best way to kill all child processes
has more info on killing child processes.
Upvotes: 1