I want to kill a whole process tree. What is the best way to do this using any common scripting languages? I am looking for a simple solution.
-
4Zombies should go away when the system reaper runs though. I'll admit that I have seen systems where zombies linger, but that's atypical. – Brian Knoblauch Dec 24 '08 at 20:00
-
10Sometimes those lingering zombies are responsible for some scary activity. – User1 Jan 07 '11 at 22:03
-
Use one of the `chronos` or `herodes` commands. – Michaël Le Barbier Sep 23 '14 at 16:18
-
9kill $(pstree
-p -a -l | cut -d, -f2 | cut -d' ' -f1) – vrdhn Apr 22 '15 at 08:56 -
@MichaelLeBarbierGrünewald Could you please link to those programs? – styrofoam fly Mar 28 '18 at 16:04
-
**TL;DR:** `ps -ej | grep whatever` then take second column which is group ID (PGID) and do `kill -- -groupID`. So if you want to kill your VPN do `ps -ej | grep vpn` then if the group ID is 1234 do `kill -- -1234`. That should kill it entirely. – Andrew Mar 19 '20 at 16:00
34 Answers
You don't say if the tree you want to kill is a single process group. (This is often the case if the tree is the result of forking from a server start or a shell command line.) You can discover process groups using GNU ps as follows:
ps x -o "%p %r %y %x %c "
If it is a process group you want to kill, just use the kill(1)
command but instead of giving it a process number, give it the negation of the group number. For example to kill every process in group 5112, use kill -TERM -- -5112
.

- 23,517
- 11
- 59
- 75

- 198,648
- 61
- 360
- 533
-
4kill -74313 -bash: kill: 74313: invalid signal specification If i add the kill -15 -GPID it worked perfectly. – Adam Peck Dec 24 '08 at 20:17
-
57As usual with almost any command, if you want a normal argument that starts with a - to not be interpreted as a switch, precede it with --: kill -- -GPID – ysth Dec 24 '08 at 21:47
-
11`pgrep` can offer an easier way to find the process group ID. For example, to kill my-script.sh's process group, run `kill -TERM -$(pgrep -o my-script.sh)`. – Josh Kelley Aug 25 '11 at 13:04
-
10Better look at http://stackoverflow.com/questions/392022/best-way-to-kill-all-child-processes/6481337#6481337 its by far a more elegant solution and if you need to list the pids of the children then use: `ps -o pid --no-headers --ppid $PARENT_PID` – Szymon Jeż Sep 15 '11 at 11:19
-
4And if you modify the format slightly and sort, you get to see all processes nicely grouped and beginning with (potentially) the group parent in each group: `ps x -o "%r %p %y %x %c" | sort -nk1,2` – haridsv Dec 03 '12 at 12:18
-
2+1 Thanks, I have been inspired from your answer to write [my answer](http://stackoverflow.com/a/15139734/938111). I have decided to write another answer because you do not explain how to get the _Process Group ID (PGID)_. When we try your answer in a shell using the _Parent Process ID (PPID)_ it works because the both (PGID and PPID) are equals. but when the tree is spawn from another process having terminated, then it fails. Cheers ;-) – oHo Feb 28 '13 at 15:52
-
2
-
4Shell scripts that do things like `prog1 & prog2 & wait` tend to not kill prog1/prog2 on Ctrl+C. You can counter that by putting this at the top of your script: `trap "kill -- -0" EXIT` which will send SIGTERM to the whole process group of that script. – unhammer Mar 08 '16 at 10:03
-
thank you for your help - please just explain for what we need the '--' before the group_id ??? – ddor254 Mar 26 '18 at 11:31
-
Kill all the processes belonging to the same process tree using the Process Group ID (PGID
)
kill -- -$PGID
Use default signal (TERM
= 15)kill -9 -$PGID
Use the signalKILL
(9)
You can retrieve the PGID
from any Process-ID (PID
) of the same process tree
kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')
(signalTERM
)kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')
(signalKILL
)
Special thanks to tanager and Speakus for contributions on $PID
remaining spaces and OSX compatibility.
Explanation
kill -9 -"$PGID"
=> Send signal 9 (KILL
) to all child and grandchild...PGID=$(ps opgid= "$PID")
=> Retrieve the Process-Group-ID from any Process-ID of the tree, not only the Process-Parent-ID. A variation ofps opgid= $PID
isps -o pgid --no-headers $PID
wherepgid
can be replaced bypgrp
.
But:grep -o [0-9]*
prints successive digits only (does not print spaces or alphabetical headers).
Further command lines
PGID=$(ps -o pgid= $PID | grep -o [0-9]*)
kill -TERM -"$PGID" # kill -15
kill -INT -"$PGID" # correspond to [CRTL+C] from keyboard
kill -QUIT -"$PGID" # correspond to [CRTL+\] from keyboard
kill -CONT -"$PGID" # restart a stopped process (above signals do not kill it)
sleep 2 # wait terminate process (more time if required)
kill -KILL -"$PGID" # kill -9 if it does not intercept signals (or buggy)
Limitation
- As noticed by davide and Hubert Kario, when
kill
is invoked by a process belonging to the same tree,kill
risks to kill itself before terminating the whole tree killing. - Therefore, be sure to run the command using a process having a different Process-Group-ID.
Long story
> cat run-many-processes.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./child.sh background &
./child.sh foreground
echo "ProcessID=$$ ends ($0)"
> cat child.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./grandchild.sh background &
./grandchild.sh foreground
echo "ProcessID=$$ ends ($0)"
> cat grandchild.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
sleep 9999
echo "ProcessID=$$ ends ($0)"
Run the process tree in background using '&'
> ./run-many-processes.sh &
ProcessID=28957 begins (./run-many-processes.sh)
ProcessID=28959 begins (./child.sh)
ProcessID=28958 begins (./child.sh)
ProcessID=28960 begins (./grandchild.sh)
ProcessID=28961 begins (./grandchild.sh)
ProcessID=28962 begins (./grandchild.sh)
ProcessID=28963 begins (./grandchild.sh)
> PID=$! # get the Parent Process ID
> PGID=$(ps opgid= "$PID") # get the Process Group ID
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 28969 Ss 33021 0:00 -bash
28349 28957 28957 28349 pts/3 28969 S 33021 0:00 \_ /bin/sh ./run-many-processes.sh
28957 28958 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./child.sh background
28958 28961 28957 28349 pts/3 28969 S 33021 0:00 | | \_ /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3 28969 S 33021 0:00 | | | \_ sleep 9999
28958 28963 28957 28349 pts/3 28969 S 33021 0:00 | | \_ /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3 28969 S 33021 0:00 | | \_ sleep 9999
28957 28959 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./child.sh foreground
28959 28960 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3 28969 S 33021 0:00 | | \_ sleep 9999
28959 28962 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3 28969 S 33021 0:00 | \_ sleep 9999
28349 28969 28969 28349 pts/3 28969 R+ 33021 0:00 \_ ps fj
The command pkill -P $PID
does not kill the grandchild:
> pkill -P "$PID"
./run-many-processes.sh: line 4: 28958 Terminated ./child.sh background
./run-many-processes.sh: line 4: 28959 Terminated ./child.sh foreground
ProcessID=28957 ends (./run-many-processes.sh)
[1]+ Done ./run-many-processes.sh
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 28987 Ss 33021 0:00 -bash
28349 28987 28987 28349 pts/3 28987 R+ 33021 0:00 \_ ps fj
1 28963 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28962 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28961 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28960 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
The command kill -- -$PGID
kills all processes including the grandchild.
> kill -- -"$PGID" # default signal is TERM (kill -15)
> kill -CONT -"$PGID" # awake stopped processes
> kill -KILL -"$PGID" # kill -9 to be sure
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 29039 Ss 33021 0:00 -bash
28349 29039 29039 28349 pts/3 29039 R+ 33021 0:00 \_ ps fj
Conclusion
I notice in this example PID
and PGID
are equal (28957
).
This is why I originally thought kill -- -$PID
was enough. But in the case the process is spawn within a Makefile
the Process ID is different from the Group ID.
I think kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)
is the best simple trick to kill a whole process tree when called from a different Group ID (another process tree).
-
If `kill` is invoked by a process belonging to the same tree, does `kill` risk to kill itself *before* terminating the whole tree? – davide Nov 12 '13 at 20:08
-
1Hi @davide. Good question. I think `kill` should always send the signal to the whole tree before receiving its own signal. But in some specific circumstances/implementations, `kill` may send to itself the signal, be interrupted, and then receive its own signal. However the risk should be enough minimal, and may be ignored in most of cases because other bugs should occur before this one. **Can this risk be ignored in your case?** Moreover other answers have this common bug (`kill` part of the process tree being killed). Hope this help.. Cheers ;) – oHo Nov 13 '13 at 07:47
-
3This only works if the sub commands themselves don't become group leaders. Even such simple tools like `man` do that. On the other hand, if you want to kill gandchild process from child process, `kill -- -$pid` won't work. So it is not generic solution. – Hubert Kario Nov 19 '13 at 19:18
-
Hum... Thanks @HubertKario for your feedback report. I will check that later and I may add a warning alert on limitation(s). Please could you provide some examples? Do you have some ideas about improvements? Please feel free to edit my answer by appending some limitation examples :) Cheers – oHo Nov 20 '13 at 09:48
-
1The example is the "child" trying to kill its children (so grandchildren of the user initiated command). IOW, try to kill the background process hierarchy in `child.sh`. – Hubert Kario Nov 20 '13 at 12:20
-
1> kill -QUIT -"$PGID" # same signal as [CRTL+C] from keyboard ---- QUIT should be replaced to INT to be true – Maxim Kholyavkin Feb 27 '14 at 08:55
-
1for OSX option --no-headers is not supported so code should be updated to: PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )" – Maxim Kholyavkin Feb 27 '14 at 09:37
-
Thanks @Speakus for your contribution. Do you agree with this re-factored answer? Cheers ;) – oHo Mar 07 '14 at 09:10
-
Hi @HubertKario I have just taken some time to refresh the answer using your useful comments. Thank you very much. Give me your feedback please ;) Cheers – oHo Mar 07 '14 at 09:38
-
@olibre i see you use much better solution as `grep -o [0-9]*` I agree :) – Maxim Kholyavkin Mar 09 '14 at 02:47
-
the specification of process group `-- -$PGID` is posix compliant, and specified here: https://pubs.opengroup.org/onlinepubs/009696799/utilities/kill.html – KFL Oct 06 '22 at 05:18
pkill -TERM -P 27888
This will kill all processes that have the parent process ID 27888.
Or more robust:
CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS
which schedule killing 33 second later and politely ask processes to terminate.
See this answer for terminating all descendants.
-
23In my quick test, pgrep only reported the immediate children, so this may not kill the entire hierarchy. – haridsv Dec 03 '12 at 12:24
-
10I agree with @haridsv: `pkill -P` sends the signal to the child only => the grandchild do not receive the signal => Therefore I have wroten [another answer](http://stackoverflow.com/a/15139734/938111) to explain that. Cheers ;-) – oHo Feb 28 '13 at 16:09
-
1From a bash script, to kill your own children, use `pkill -TERM -P ${$}`. – François Beausoleil Apr 10 '15 at 14:30
-
3@Onlyjob, isn't it dangerous to sleep then kill? The process IDs may have been reused by the OS in the meantime: you might be killing processes which aren't your children anymore. I suspect the call to pkill would have to be done again to ensure against this. – François Beausoleil Apr 10 '15 at 14:42
-
To kill after sleep is safe because PIDs are not re-used by the system. I'm not sure if it is theoretically possible but at least you can be certain that PIDs will not be re-used within this short time frame. – Onlyjob Apr 13 '15 at 01:25
-
-
1@Onlyjob _PIDs will not be re-used within this short time frame._, you can't be so sure on a busy system. – cychoi Jul 15 '15 at 17:27
-
-
@cychoi, yes I can be sure. What are chances for "busy" system to create over 30000 processes within given time frame? See https://stackoverflow.com/questions/11323410/linux-pid-recycling – Onlyjob Jul 16 '15 at 01:42
-
2@Onlyjob FYI, I am able to spawn 32768 processes, single threaded, with light I/O access in less than 19 seconds on my machine: `$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s` – cychoi Jul 16 '15 at 02:56
-
Of course you can. :) Yet 1000 new processes per second for over 30 seconds would be rather unusual (although _some_ risk is there). Do you recall any well known applications that usually create that many processes in less than a minute? – Onlyjob Jul 17 '15 at 07:15
-
@Onlyjob: You don't need 32000 new processes. You need 32000 *total* processes, counting background processes that are not dying. If somehow you had 29k processes running, then you'd start seeing PID repeats after only 3000 short-lived processes. – Ben Voigt Sep 14 '15 at 21:40
-
Valid concern, but it is easy to address by something like `sysctl kernel.pid_max=262144` or `kernel.pid_max = 262144` added to `/etc/sysctl.conf`. – Onlyjob Nov 26 '15 at 00:31
To kill a process tree recursively, use killtree():
#!/bin/bash
killtree() {
local _pid=$1
local _sig=${2:--TERM}
kill -stop ${_pid} # needed to stop quickly forking parent from producing children between child killing and parent killing
for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
killtree ${_child} ${_sig}
done
kill -${_sig} ${_pid}
}
if [ $# -eq 0 -o $# -gt 2 ]; then
echo "Usage: $(basename $0) <pid> [signal]"
exit 1
fi
killtree $@
-
3The `--` arguments to `ps` don't work on OS X. To make it work there replace the `ps` command by: `ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g` where `_regex` is defined before the `for` loop: `local _regex="[ ]*([0-9]+)[ ]+${_pid}"` – artur Oct 02 '11 at 22:21
-
6Stopped processes don't get killed with SIGTERM. See my [answer](http://stackoverflow.com/a/13481601/52499) – x-yuri Nov 20 '12 at 20:32
-
1-1 uses #!/bin/bash instead of #!/usr/bin/env bash (or better yet POSIX only constructs and /bin/sh) – Good Person Feb 28 '13 at 16:33
-
Would it suffice to send a SIGKILL in place of SIGTERM to guarantee that `killtree()` works reliably? – davide Oct 30 '13 at 10:15
-
3if `ps` doesn't support `--ppid`, one can use `pgrep -P ${_pid}` instead – Hubert Kario Nov 19 '13 at 19:07
-
@HubertKario Indeed, that makes it work on both unix and osx with the same script. (As long as pgrep is installed on osx ofc - can be done with 'brew install proctools'). – Zitrax Feb 19 '14 at 11:13
-
@GoodPerson - did you really downvote this for the reasons given ? I note it's tagged for bash – Brian Agnew Mar 09 '16 at 16:25
-
-
To support calling this in the script itself, I do `ps` twice and use `comm` to intersect the list - preventing the `ps` reporting its own PID in the first loop. local _list1=$(ps -o pid --no-headers --ppid ${_pid} | sort); local _list2=$(ps -o pid --no-headers --ppid ${_pid} | sort); local _list=$(comm -12 <(cat <<< "$_list1") <(cat <<< "$_list2")); – HelloSam Oct 27 '16 at 09:06
-
-
Thanks, this actually good solution some how kill user group didn't work for me. – Tymur Kubai aka SirDiR Oct 18 '20 at 13:55
-
2Finally the solution that really works: kills the entire tree of descendants (while `kill` & `pkill` didn't) – Ed'ka Oct 15 '21 at 21:55
-
I checked and unfortunately `pslist` is a Perl script that just calls out to `ps` to find the children of a process. Not very efficient. You can do much better by just manually reading every file in `/proc/*/status`, which is what tools like `pstree` do. Unfortunately that is vulnerable to races. This is really something Linux itself needs to do. However this does at least *try* to answer the question rather than giving up and relying on process groups or session IDs, so I upvoted. – Timmmm Jul 05 '23 at 12:15
I use a little bit modified version of a method described here: https://stackoverflow.com/a/5311362/563175
So it looks like that:
kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`
where 24901 is parent's PID.
It looks pretty ugly but does it's job perfectly.

- 1
- 1

- 359
- 4
- 11
-
1Simplifying with grep, instead of sed...`pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'` – anishsane Jun 14 '13 at 17:42
-
2you should add `-l` to `pstree`, so long lines dont get truncated; it can be made simpler to read also with `kill \`pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'\`` (no need to convert `\n` to space as it will work fine), thx! – Aquarius Power Oct 13 '14 at 18:35
Modified version of zhigang's answer:
#!/usr/bin/env bash
set -eu
killtree() {
local pid
for pid; do
kill -stop $pid
local cpid
for cpid in $(pgrep -P $pid); do
killtree $cpid
done
kill $pid
kill -cont $pid
wait $pid 2>/dev/null || true
done
}
cpids() {
local pid=$1 options=${2:-} space=${3:-}
local cpid
for cpid in $(pgrep -P $pid); do
echo "$space$cpid"
if [[ "${options/a/}" != "$options" ]]; then
cpids $cpid "$options" "$space "
fi
done
}
while true; do sleep 1; done &
cpid=$!
for i in $(seq 1 2); do
cpids $$ a
sleep 1
done
killtree $cpid
echo ---
cpids $$ a

- 16,722
- 15
- 114
- 161
-
you can `wait $pid` only on processes you've started, not all precesses, so this is not a generic solution – Hubert Kario Jan 19 '14 at 13:15
-
@Hubert Kario In that case, wait will just exit with non-zero status and continue executing the script. Am I wrong? But `wait` will suppress `Terminated` message if it's a child. – x-yuri Jan 20 '14 at 13:49
I can't comment (not enough reputation), so I am forced to add a new answer, even though this is not really an answer.
There is a slight problem with the otherwise very nice and thorough answer given by @olibre on Feb 28. The output of ps opgid= $PID
will contain leading spaces for a PID shorter than five digits because ps
is justifying the column (right align the numbers). Within the entire command line, this results in a negative sign, followed by space(s), followed by the group PID. Simple solution is to pipe ps
to tr
to remove spaces:
kill -- -$( ps opgid= $PID | tr -d ' ' )

- 189
- 2
- 10
brad's answer is what I'd recommend too, except that you can do away with awk
altogether if you use the --ppid
option to ps
.
for child in $(ps -o pid -ax --ppid $PPID) do ....... done

- 57,116
- 41
- 173
- 227

- 41,826
- 12
- 125
- 142
-
This doesn't work for me unless I take out the -ax, for some reason (Centos5). Otherwise this is great! – xitrium Aug 05 '10 at 09:03
if you know pass the pid of the parent process, here's a shell script that should work:
for child in $(ps -o pid,ppid -ax | \
awk "{ if ( \$2 == $pid ) { print \$1 }}")
do
echo "Killing child process $child because ppid = $pid"
kill $child
done

- 264
- 1
- 4
-
Some versions of ps will throw a warning if you use "-ax" instead of "ax". Thus: for child in $(ps -o pid,ppid ax | \ awk "{ if ( \$2 == $pid ) { print \$1 }}") – Cory R. King May 20 '13 at 20:45
To add to Norman Ramsey's answer, it may be worth looking at at setsid if you want to create a process group.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html
The setsid() function shall create a new session, if the calling process is not a process group leader. Upon return the calling process shall be the session leader of this new session, shall be the process group leader of a new process group, and shall have no controlling terminal. The process group ID of the calling process shall be set equal to the process ID of the calling process. The calling process shall be the only process in the new process group and the only process in the new session.
Which I take to mean that you can create a group from the starting process. I used this in php in order to be able to kill a whole process tree after starting it.
This may be a bad idea. I'd be interested in comments.

- 2,848
- 2
- 22
- 20
-
Actually this is a great idea and works very well. I'm using it in cases where I can put processes in the same process group (or they're allready in the same group). – ku1ik Jan 11 '13 at 00:32
Inspired by ysth’s comment
kill -- -PGID
instead of giving it a process number, give it the negation of the group number. As usual with almost any command, if you want a normal argument that starts with a
-
to not be interpreted as a switch, precede it with--
-
Oops, I have just realized I have given [the same answer](http://stackoverflow.com/a/15139734/938111) as you => +1. But moreover I explain how to simply get `PGID` from `PID`. What do you think? Cheers – oHo Mar 01 '13 at 09:12
It's super easy to do this with python using psutil. Just install psutil with pip and then you have a full suite of process manipulation tools:
def killChildren(pid):
parent = psutil.Process(pid)
for child in parent.get_children(True):
if child.is_running():
child.terminate()

- 369
- 2
- 5
Based on zhigang's answer, this avoids self-killing:
init_killtree() {
local pid=$1 child
for child in $(pgrep -P $pid); do
init_killtree $child
done
[ $pid -ne $$ ] && kill -kill $pid
}

- 2,082
- 3
- 21
- 30
The following shell function is similar to many of the other answers, but it works both on Linux and BSD (OS X, etc) without external dependencies like pgrep
:
killtree() {
local parent=$1 child
for child in $(ps -o ppid= -o pid= | awk "\$1==$parent {print \$2}"); do
killtree $child
done
kill $parent
}

- 1,786
- 15
- 20
-
-
@mato – That's not extra. It limits the scope of `$child` to that function so as to not disturb other (non-local) variables with the same name and to ensure the local variable's value is cleaned up after the function ends. – Adam Katz Feb 27 '17 at 19:30
-
Oh, I see now, I thought it was part of assignment. I guess I should re-read (slower) before writing a comment. :) Thanks. – mato Feb 28 '17 at 09:29
-
Btw, would it not be better to create a list of children and then start killing from the top (parent) ? .. Thus we could avoid a situation when a parent recreates a child or continues running next code which could potentially alter intended behaviour. – mato Feb 28 '17 at 09:32
If you want to kill a process by name:
killall -9 -g someprocessname
or
pgrep someprocessname | xargs pkill -9 -g

- 3,782
- 1
- 28
- 33
Here is a variation of @zhigang's answer which does without AWK, relying only on Bash's native parsing possibilities:
function killtree {
kill -STOP "$1"
ps -e -o pid= -o ppid= | while read -r pid ppid
do
[[ $ppid = $1 ]] || continue
killtree "$pid" || true # Skip over failures
done
kill -CONT "$1"
kill -TERM "$1"
}
It seems to work fine on both Macs and Linux. In situations where you can't rely on being able to manage process groups -- like when writing scripts for testing a piece of software which must be built in multiple environments -- this tree-walking technique is definitely helpful.

- 1,631
- 16
- 26
This is my version of killing all the child processes using bash script. It does not use recursion and depends on pgrep command.
Use
killtree.sh PID SIGNAL
Contents of killtrees.sh
#!/bin/bash
PID=$1
if [ -z $PID ];
then
echo "No pid specified"
fi
PPLIST=$PID
CHILD_LIST=`pgrep -P $PPLIST -d,`
while [ ! -z "$CHILD_LIST" ]
do
PPLIST="$PPLIST,$CHILD_LIST"
CHILD_LIST=`pgrep -P $CHILD_LIST -d,`
done
SIGNAL=$2
if [ -z $SIGNAL ]
then
SIGNAL="TERM"
fi
#do substring from comma to space
kill -$SIGNAL ${PPLIST//,/ }

- 481
- 4
- 10
-
2This will fail, if new processes are created after the child list has been build. – ceving Nov 20 '19 at 09:13
The following has been tested on FreeBSD, Linux and MacOS X and only depends on pgrep and kill (the ps -o versions don't work under BSD). First argument is parent pid of which children have to be terminated. second argument is a boolean to determine whether the parent pid has to be terminated too.
KillChilds() {
local pid="${1}"
local self="${2:-false}"
if children="$(pgrep -P "$pid")"; then
for child in $children; do
KillChilds "$child" true
done
fi
if [ "$self" == true ]; then
kill -s SIGTERM "$pid" || (sleep 10 && kill -9 "$pid" &)
fi
}
KillChilds $$ > /dev/null 2>&1
This will send SIGTERM to any child / grandchild process within a shell script and if SIGTERM doesn't succeed, it will wait 10 seconds and then send kill.
Earlier answer:
The following also works but will kill the shell itself on BSD.
KillSubTree() {
local parent="${1}"
for child in $(ps -o pid=$parent); do
if [ $$ -ne $child ]; then (kill -s SIGTERM $child || (sleep 10 && kill -9 $child & )) > /dev/null 2>&1 ; fi
done
}
# Example lanch from within script
KillSubTree $$ > /dev/null 2>&1

- 2,819
- 1
- 26
- 48
if you have pstree and perl on your system, you can try this:
perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)'

- 183
- 1
- 5
I develop the solution of zhigang, xyuri and solidsneck further:
#!/bin/bash
if test $# -lt 1 ; then
echo >&2 "usage: kiltree pid (sig)"
exit 1 ;
fi ;
_pid=$1
_sig=${2:-TERM}
# echo >&2 "killtree($_pid) mypid = $$"
# ps axwwf | grep -6 "^[ ]*$_pid " >&2 ;
function _killtree () {
local _children
local _child
local _success
if test $1 -eq $2 ; then # this is killtree - don't commit suicide!
echo >&2 "killtree can´t kill it´s own branch - some processes will survive." ;
return 1 ;
fi ;
# this avoids that children are spawned or disappear.
kill -SIGSTOP $2 ;
_children=$(ps -o pid --no-headers --ppid $2) ;
_success=0
for _child in ${_children}; do
_killtree $1 ${_child} $3 ;
_success=$(($_success+$?)) ;
done ;
if test $_success -eq 0 ; then
kill -$3 $2
fi ;
# when a stopped process is killed, it will linger in the system until it is continued
kill -SIGCONT $2
test $_success -eq 0 ;
return $?
}
_killtree $$ $_pid $_sig
This version will avoid killing its ancestry - which causes a flood of child processes in the previous solutions.
Processes are properly stopped before the child list is determined, so that no new children are created or disappear.
After being killed, the stopped jobs have to be continued to disappear from the system.

- 51
- 2
Thanks for your wisdom, folks. My script was leaving some child processes on exit and the negation tip made things easier. I wrote this function to be used in other scripts if necessary:
# kill my group's subprocesses: killGroup
# kill also myself: killGroup -x
# kill another group's subprocesses: killGroup N
# kill that group all: killGroup -x N
# N: PID of the main process (= process group ID).
function killGroup () {
local prid mainpid
case $1 in
-x) [ -n "$2" ] && kill -9 -$2 || kill -9 -$$ ;;
"") mainpid=$$ ;;
*) mainpid=$1 ;;
esac
prid=$(ps ax -o pid,pgid | grep $mainpid)
prid=${prid//$mainpid/}
kill -9 $prid 2>/dev/null
return
}
Cheers.
Old question, I know, but all the responses seem to keep calling ps, which I didn't like.
This awk-based solution doesn't require recursion and only calls ps once.
awk 'BEGIN {
p=1390
while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2
o=1
while (o==1) {
o=0
split(p, q, " ")
for (i in q) if (a[q[i]]!="") {
p=p""a[q[i]]
o=1
a[q[i]]=""
}
}
system("kill -TERM "p)
}'
Or on a single-line:
awk 'BEGIN {p=1390;while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2;o=1;while (o==1) {o=0;split(p, q, " ");for (i in q) {if (a[q[i]]!="") {p=p""a[q[i]];o=1;a[q[i]]=""}}}system("kill -TERM "p)}'
Basically the idea is that we build up an array (a) of parent:child entries, then loop around the array finding children for our matching parents, adding them to our parents list (p) as we go.
If you don't want to kill the top-level process, then doing
sub(/[0-9]*/, "", p)
just before the system() line would remove it from the kill set.
Bear in mind that there is a race condition here, but that's true (as far as I can see) of all of the solutions. It does what I needed because the script I needed it for doesn't create lots of short-lived children.
An exercise for the reader would be to make it a 2-pass loop: after the first pass, send SIGSTOP to all processes in the p list, then loop to run ps again and after the second pass send SIGTERM, then SIGCONT. If you don't care about nice endings then second-pass could just be SIGKILL, I suppose.

- 79
- 5
It is probably better to kill the parent before the children; otherwise the parent may likely spawn new children again before he is killed himself. These will survive the killing.
My version of ps is different from that above; maybe too old, therefore the strange grepping...
To use a shell script instead of a shell function has many advantages...
However, it is basically zhigangs idea
#!/bin/bash
if test $# -lt 1 ; then
echo >&2 "usage: kiltree pid (sig)"
fi ;
_pid=$1
_sig=${2:-TERM}
_children=$(ps j | grep "^[ ]*${_pid} " | cut -c 7-11) ;
echo >&2 kill -${_sig} ${_pid}
kill -${_sig} ${_pid}
for _child in ${_children}; do
killtree ${_child} ${_sig}
done

- 27,509
- 17
- 111
- 155

- 27
- 1
-
note that @zhighang script SIGSTOPs the parent process and delivers signal to stopped process, so this shouldn't (AFAIK) cause a race condition between process creating children and signal delivery. Your version though has race between getting the list of children and signal delivery to parent. – Hubert Kario Nov 20 '13 at 12:31
To kill the whole process group at once, just like ^C
does:
PID="$(pgrep -f unique_command_line_part)"
if [[ -n "$PID" ]]
then
PGID="$(ps --no-headers -p $PID -o pgid)"
kill -SIGINT -- -${PGID// /}
fi
Each line is explained in this answer

- 2,321
- 19
- 12
In sh the jobs command will list the background processes. In some cases it might be better to kill the newest process first, e.g. the older one created a shared socket. In those cases sort the PIDs in reverse order. Sometimes you want to wait moment for the jobs to write something on disk or stuff like that before they stop.
And don't kill if you don't have to!
for SIGNAL in TERM KILL; do
for CHILD in $(jobs -s|sort -r); do
kill -s $SIGNAL $CHILD
sleep $MOMENT
done
done

- 21
Killing child process in shell script:
Many time we need to kill child process which are hanged or block for some reason. eg. FTP connection issue.
There are two approaches,
1) To create separate new parent for each child which will monitor and kill child process once timeout reached.
Create test.sh as follows,
#!/bin/bash
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
for CMD in ${CMDs[*]}; do
(sleep 10 & PID=$!; echo "Started $CMD => $PID"; sleep 5; echo "Killing $CMD => $PID"; kill $PID; echo "$CMD Completed.") &
done
exit;
and watch processes which are having name as 'test' in other terminal using following command.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Above script will create 4 new child processes and their parents. Each child process will run for 10sec. But once timeout of 5sec reach, thier respective parent processes will kill those childs. So child won't be able to complete execution(10sec). Play around those timings(switch 10 and 5) to see another behaviour. In that case child will finish execution in 5sec before it reaches timeout of 10sec.
2) Let the current parent monitor and kill child process once timeout reached. This won't create separate parent to monitor each child. Also you can manage all child processes properly within same parent.
Create test.sh as follows,
#!/bin/bash
declare -A CPIDs;
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
CMD_TIME=15;
for CMD in ${CMDs[*]}; do
(echo "Started..$CMD"; sleep $CMD_TIME; echo "$CMD Done";) &
CPIDs[$!]="$RN";
sleep 1;
done
GPID=$(ps -o pgid= $$);
CNT_TIME_OUT=10;
CNT=0;
while (true); do
declare -A TMP_CPIDs;
for PID in "${!CPIDs[@]}"; do
echo "Checking "${CPIDs[$PID]}"=>"$PID;
if ps -p $PID > /dev/null ; then
echo "-->"${CPIDs[$PID]}"=>"$PID" is running..";
TMP_CPIDs[$PID]=${CPIDs[$PID]};
else
echo "-->"${CPIDs[$PID]}"=>"$PID" is completed.";
fi
done
if [ ${#TMP_CPIDs[@]} == 0 ]; then
echo "All commands completed.";
break;
else
unset CPIDs;
declare -A CPIDs;
for PID in "${!TMP_CPIDs[@]}"; do
CPIDs[$PID]=${TMP_CPIDs[$PID]};
done
unset TMP_CPIDs;
if [ $CNT -gt $CNT_TIME_OUT ]; then
echo ${CPIDs[@]}"PIDs not reponding. Timeout reached $CNT sec. killing all childern with GPID $GPID..";
kill -- -$GPID;
fi
fi
CNT=$((CNT+1));
echo "waiting since $b secs..";
sleep 1;
done
exit;
and watch processes which are having name as 'test' in other terminal using following command.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Above script will create 4 new child processes. We are storing pids of all child process and looping over them to check if they are finished their execution or still running. Child process will execution till CMD_TIME time. But if CNT_TIME_OUT timeout reach , All children will get killed by parent process. You can switch timing and play around with script to see behavior. One drawback of this approach is , it is using group id for killing all child tree. But parent process itself belong to same group so it will also get killed.
You may need to assign other group id to parent process if you don’t want parent to be killed.
More details can be found here,

- 11,888
- 3
- 47
- 79

- 2,386
- 20
- 14
This script also work:
#/bin/sh
while true
do
echo "Enter parent process id [type quit for exit]"
read ppid
if [ $ppid -eq "quit" -o $ppid -eq "QUIT" ];then
exit 0
fi
for i in `ps -ef| awk '$3 == '$ppid' { print $2 }'`
do
echo killing $i
kill $i
done
done

- 1,895
- 20
- 26
I know that is old, but that is the better solution that i found:
killtree() {
for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
echo Terminating: $p
kill $p
done
}

- 186
- 1
- 12
Kill The Group Using Only Name of Process Which Belongs to Group:
kill -- -$(ps -ae o pid,pgrp,cmd | grep "[e]xample.py" | awk '{print $2}' | tail -1)
This is a modification of olibre's answer but you don't need to know the PID, just the name of a member of the group.
Explanation:
To get the group id you do the ps command using the arguments as shown, grep it for your command, but formatting example.py with quotes and using the bracket for the first letter (this filters out the grep command itself) then filter it through awk to get the second field which is the group id. The tail -1 gets rid of duplicate group ids. You put all of that in a variable using the $() syntax and voila – you get the group id. So you substitute that $(mess) for that -groupid above.
If you know the pid of the thing you want to kill, you can usually go from the session id, and everything in the same session. I'd double check, but I used this for scripts starting rsyncs in loops that I want to die, and not start another (because of the loop) as it would if I'd just killall'd rsync.
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))
If you don't know the pid you can still nest more
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))

- 4,143
- 27
- 35
And now for some clever shell programming.
There is a cost with this solution, but at least it is based on everyday iteration and recursion. This can be converted to bash by paying careful attention to the typeset
commands and converting them to declare
or local
where appropriate.
Discussion
When killing a process, one must deal with the realty that it could be a parent to many children, and that each child could be the parent of even more children, and on, and on, an on.
What to do?
If only there was a function to test if a process had children, and another function to return the child PIDs of a parent process.
Then, the game would be much simpler, because you could create a loop to iterate over a list of PIDs, checking each one for children before killing it. If there are no children, just kill the process. If there are children, call the driving function recursively and pass it the results of a function that gets the PIDs of a parent's children.
The base case action (process does not have children).
#!/bin/ksh
function killProcess ()
{
typeset -r PID=$1
if [[ ! isProcess $PID ]]
then
echo -e "Process $PID cannot be terminated because it does not exist.\n" 1>&2
return 1
elif [[ kill -s TERM $PID ]] && [[ ! isProcess $PID ]]
then
echo -e "Process $PID was terminated.\n" 1>&2
return 0
elif kill -s KILL $PID
echo -e "Process $PID killed with SIGKILL (9) signal. No time to clean up potential files.\n" 1>&2
return 0
elif isZombie $PID
then
echo -e "Process $PID in the zombie status.\n" 1>&2
return 2
else
echo -e "Process $PID is alive. SIGTERM and SIGKILL had no effect. It is not a zombie.\n" 1>&2
fi
return 3
}
function attemptToKillPid ()
{
typeset -r PID=$1
if killProcess $PID
then
return 0
fi
ppid=$(getParentPid $pid)
echo -e "Process $pid of parent $ppid was not able to be killed.\n" 1>&2
return 1
}
The general case action (process has children).
function killPidFamily ()
{
typeset -r PROCESSES=$*
typeset -ir NUM_PROCESSES_TO_KILL=$(countLines $PROCESSES)
typeset -i numKilledProcesses=0
typeset ppid
for pid in $PROCESSES
do
pid=$(trim $pid)
if ! hasChildPids $pid
then
attemptToKillPid $pid && (( numKilledProcesses++ ))
else
killPidFamily $(getChildPids $pid) && attemptToKillPid $pid && (( numKilledProcesses++ ))
fi
done
(( numKilledProcesses == NUM_PROCESSES_TO_KILL ))
return $?
}
Library of supporting functions.
#!/bin/ksh
function trim ()
{
echo -n "$1" | tr -d [:space:]
}
function countLines ()
{
typeset -r $LIST=$*
trim $(echo $LIST | wc -l | awk {'print $1'})
}
function getProcesses ()
{
# NOTE: -o pgid below would be $4 in awk.
ps -e -o comm,pid,ppid,pgid,user,ruid,euid,group,rgid,egid,etime,etimes,stat --no-headers
}
function getProcess ()
{
typeset -r PID=$1
ps -p $PID -o comm,pid,ppid,pgid,user,ruid,euid,group,rgid,egid,etime,etimes,stat --no-headers
}
function isProcess ()
{
typeset -r PID=$1
ps -p $PID -o pid --no-headers 1>&2
return $?
}
function getProcessStatus ()
{
typeset -r PID=$1
trim $(ps -p $PID -o stat --no-headers)
}
function isZombie ()
{
typeset -r PID=$1
typeset processStatus
processStatus=$(getProcessStatus $PID)
[[ "$processStatus" == "Z" ]]
return $?
}
function hasChildPids ()
{
typeset -r PPID=$1
echo $(getProcesses) | awk '{print $3}' | sort -n | uniq | grep "^${PPID}$"
return $?
}
function getChildPids ()
{
typeset -r PPID=$1
echo $(getProcesses) | awk '{print $2, $3}' | sort -k 2 | awk "\$2 == $PPID {print \$1}" | sort -n
}
function getParentPid ()
{
typeset -r PID=$1
trim $(echo $(getProcess $PID) | awk '{print $3}')
}
In this way, you know for sure that the process tree is being destroyed from the branches, moving up to the root. This helps avoid the potential of creating zombies and other undesirable situations.
Now, that you have seen the most expensive way to do this (killing one process at a time), investigate how you could alter this solution to use the PGID (process group ID). The getProcesses ()
function already prints the PGID ($4
in awk
), so learn how to use it, or don't.

- 6,980
- 2
- 39
- 44
type ps -ef
check the process id.
Kill the process by typing kill -9 <pid>

- 642
- 6
- 8