This is one of those ones where I clearly should have read the question more carefully (though seemingly this is the case with most answers on to this question). I have left the original answer intact because it gives some good information, even though it clearly misses the point of the question.
I think the most general, robust approach here (at least for Linux) is to use SID (Session ID) rather than PPID or PGID. This is much less likely to be changed by child processes and, in the case of shell script, the
setsid command can be used to start a new session. Outside of the shell the
setuid system call can be used.
For a shell that is a session leader, you can kill all the other processes in the session by doing (the shell won't kill itself):
kill $(ps -s $$ -o pid=)
Note: The trailing equals sign in argument
pid= removes the
PID column header.
Otherwise, using system calls, call
getsid for each process seems like the only way.
Using a PID namespace
This is the most robust approach, however the downsides are that it is Linux only and that it needs root privileges. Also the shell tools (if used) are very new and not widely available.
For a more detailed discussion of PID namespaces, please see this question - Reliable way to jail child processes using `nsenter:`. The basic approach here is that you can create a new PID namespace by using the
CLONE_NEWPID flag with the
clone system call (or via the
When a process in a PID namespace is orphaned (ie when it parent process finishes), it is re-parented to the top level PID namespace process rather than the
init. This means that you can always identify all the descendants of the top level process by walking the process tree. In the case of a shell script the PPID approach below would then reliably kill all descendants.
Further reading on PID namespaces:
Killing child processes
The easy way to do this in a shell script, provided
pkill is available is:
pkill -P $$
This kills all children of the current given process (
$$ expands to the PID of the current shell).
Killing all descendent processes
Another situation is that you may want to kill all the descendants of the current shell process as well as just the direct children. In this case you can use the recursive shell function below to list all the descendant PIDs, before passing them as arguments to kill:
local children=$(ps -o pid= --ppid "$1")
for pid in $children
kill $(list_descendants $$)
One thing to beware of, which might prevent the above from working as expected is the double
fork() technique. This is commonly used when daemonising a process. As the name suggests the process that is to be started runs in the second fork of the original process. Once the process is started, the first fork then exits meaning that the process becomes orphaned.
In this case it will become a child of the
init process instead of the original process that it was started from. There is no robust way to identify which process was the original parent, so if this is the case, you can't expect to be able to kill it without having some other means of identification (a PID file for example). However, if this technique has been used, you shouldn't try to kill the process without good reason.