How can I get the entire process tree spawned by a given process displayed as a tree and only that tree i.e. no other processes?

The output could e.g. look like

 4378 ?        Ss     0:10 SCREEN
 4897 pts/16   Ss     0:00  \_ -/bin/bash
25667 pts/16   S+     0:00  |   \_ git diff
25669 pts/16   S+     0:00  |       \_ less -FRSX
11118 pts/32   Ss+    0:00  \_ -/bin/bash
11123 pts/32   S+     0:00      \_ vi

I couldn't get the desired result purely with parameters to ps.

The following gives the desired result but seems a bit involved:


pidtree() {
  echo -n $1 " "
  for _child in $(ps -o pid --no-headers --ppid $1); do
    echo -n $_child `pidtree $_child` " "

ps f `pidtree 4378`

Does anyone have an easier solution?

  • Not an answer, but start with ps auxf.
    – jftuga
    Nov 30 '11 at 21:34
  • 3
    @jtfuga This is in fact where I started, but this gives me all processes, which is exactly what I don't want.
    – kynan
    Dec 1 '11 at 9:59

13 Answers 13


The pstree is a very good solution, but it is a little bit reticent. I use ps --forest instead. But not for a PID (-p) because it prints only the specific process, but for the session (-g). It can print out any information ps can print in a fancy ASCII art tree defining the -o option.

So my suggestion for this problem:

ps --forest -o pid,tty,stat,time,cmd -g 2795

If the process is not a session leader, then a little bit more trick has to be applied:

ps --forest -o pid,tty,stat,time,cmd -g $(ps -o sid= -p 2795)

This gets the session id (SID) of the current process first and then call ps again with that sid.

If the column headers are not needed add a '=' after each column definition in '-o' options, like:

ps --forest -o pid=,tty=,stat=,time=,cmd= -g $(ps -o sid= -p 2795)

An example run and the result:

$ ps --forest -o pid=,tty=,stat=,time=,cmd= -g $(ps -o sid= -p 30085)
27950 pts/36   Ss   00:00:00 -bash
30085 pts/36   S+   00:00:00  \_ /bin/bash ./loop.sh
31888 pts/36   S+   00:00:00      \_ sleep 5

Unfortunately this does not work for screen as it sets the sid for each child screen and all grandchild bash.

To get all the processes spawned by a process the whole tree needs to be built. I used for that. At first it builds a hash array to contain all PID => ,child,child... . At the end it calls a recursive function to extract all the child processes of a given process. The result is passed to another ps to format the result. The actual PID has to be written as an argument to instead of <PID>:

ps --forest $(ps -e --no-header -o pid,ppid|awk -vp=<PID> 'function r(s){print s;s=a[s];while(s){sub(",","",s);t=s;sub(",.*","",t);sub("[0-9]+","",s);r(t)}}{a[$2]=a[$2]","$1}END{r(p)}')

For a SCREEN process (pid=8041) the example output looks like this:

 8041 ?        Ss     0:00 SCREEN
 8042 pts/8    Ss     0:00  \_ /bin/bash
 8092 pts/8    T      0:00      \_ vim test_arg test_server
12473 pts/8    T      0:00      \_ vim
12972 pts/8    T      0:00      \_ vim
  • The issue with this (I know, it's an old answer) is the --forest option only works on Linux (or other gnu based "ps" commands). Solaris, and MacOS don't like it.
    – Armand
    Jul 22 '17 at 3:03
  • @TrueY do you know of a C API that can do that ? Thank you
    – Bionix1441
    Sep 20 '17 at 7:45
  • Bionix No, sorry... I may read /proc/<PID> directories to build that tree.
    – TrueY
    Sep 20 '17 at 13:44
  • 2
    Seems like this should be simple if ps had a filter flag option just to filter to all descendants of a PID. Sep 17 '18 at 4:33
  • Regarding your last awk oneliner, I wonder if it has to be that complex?
    – x-yuri
    Mar 28 '20 at 15:54
pstree ${pid}

where ${pid} is the pid of the parent process.

On Gentoo Linux, pstree is in the package "psmisc," apparently located at http://psmisc.sourceforge.net/

  • 10
    Thanks, should have mentioned that I had looked at pstree, but missed a more verbose output format. However, pstree -p <pid> at least print the pids which is reasonably close.
    – kynan
    Dec 1 '11 at 10:00
  • 3
    I have this problem too, I need to gather all child pids recursively but I need only the pids, so I would have to sed all that.. mmm this works :) pstree -pn 4008 |grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" Jun 17 '14 at 23:36

Here is my version that runs instantly (because ps executed only once). Works in bash and zsh.

pidtree() (
    [ -n "$ZSH_VERSION"  ] && setopt shwordsplit
    declare -A CHILDS
    while read P PP;do
        CHILDS[$PP]+=" $P"
    done < <(ps -e -o pid= -o ppid=)

    walk() {
        echo $1
        for i in ${CHILDS[$1]};do
            walk $i

    for i in "$@";do
        walk $i
  • 1
    This also prints out the pid of the current process, which you may or may not want. I didn't want the current process listed (just descendants of the current process), so I changed the script by moving echo $1 inside the first for loop and changing it to echo $i. Aug 16 '16 at 18:29
  • 1
    I was just about to write such a function when I found this. This is the only solution that seems to work on MacOS, Linux, and Solaris. Great work. Love that you rely on a single run of ps to get the job done in memory.
    – Armand
    Jul 22 '17 at 3:01

I've created a small bash script to create a list pid's of a parent's child process(es). Recursively till it finds the last child process which does not have any childs. It does not give you a tree view. It just lists all pid's.

function list_offspring {
  tp=`pgrep -P $1`          #get childs pids of parent pid
  for i in $tp; do          #loop through childs
    if [ -z $i ]; then      #check if empty list
      exit                  #if empty: exit
    else                    #else
      echo -n "$i "         #print childs pid
      list_offspring $i     #call list_offspring again with child pid as the parent
list_offspring $1

first argument of list_offspring is the parent pid

  • 1
    There are already several other answers that give a bash script that doesn't print an actual tree, including one in the question itself. What advantages does your (partial) answer have over the existing (partial) answers? Jun 17 '15 at 10:34
  • 1
    I used recursion and added some explanation. Thought it might help someone. It does exactly what the title asks for.
    – Merijn
    Jun 17 '15 at 12:10
  • I like the use of pgrep here, since ps can differ among unix variants. Aug 2 '16 at 11:08

ps -H -g "$pid" -o comm

doesn't add a tree per se, it is just the list of processes.

gives for example

  • The -H option is used to display a process tree or "process hierarchy (forest)" Aug 31 '16 at 16:04

I have been working to find a solution to the exact same problem. Bascially, ps manpage does not document any option allowing to do what we want with a single command. Conclusion: a script is needed.

I came up with a script very similar to yours. I pasted it in my ~/.bashrc so I can use it from any shell.

pidtree() {
  local parent=$1
  local list=
  while [ "$parent" ] ; do     
    if [ -n "$list" ] ; then
    parent=$(ps --ppid $parent -o pid h)
  ps -f -p $list f

This gives only the pid+children pids each on one line which is useful for further processing.

pidtree() {
    for _pid in "$@"; do
        echo $_pid
        pidtree `ps --ppid $_pid -o pid h`

With nicer tree layout:

_pidtree() {
    local p="$1"
    for _pid in "$@"; do
        echo "$p$_pid"
        _pidtree " $p" `ps --ppid $_pid -o pid h`
pidtree() {
    _pidtree '' "$@"
  • Simple and splendid. Feb 13 '20 at 18:52

On way on the command-line:

ps -o time,pid,ppid,cmd --forest -g -p $(pgrep -x bash)

it outputs:

00:00:00  5484  5480 bash
00:00:01  5531  5484  \_ emacs -nw .bashrc
00:00:01  2986  2984 /bin/bash
00:00:00  4731  2986  \_ redshift
00:00:00  5543  2986  \_ ps -o time,pid,ppid,cmd --forest -g -p 2986 5484

more elegant way of that way is to define a function in .bashrc:

function subps()                                                                                    
    process=$(pgrep -x $1)                                                                                                                                                                     
    ps -o time,pid,ppid,cmd --forest -g -p $process                                                 

then on the command-line run:

subps bash

You can get the pids of all child processes of a given parent process <pid> by reading the /proc/<pid>/task/<tid>/children entry.

This file contains the pids of first-level child processes. Recursively you can see for children too.

For more information head over to https://lwn.net/Articles/475688/


This gets all pids with the ppid last, filters on those ending with the ppid we're interested in, then just selects the pid. Then recurses.

pidtree() {
  echo $1
  for p in $(ps -o pid=,ppid= | grep $1$ | cut -f1 -d' '); do
    pidtree $p

pidtree $1

I made a similar script based on Philippe's above

pidlist() {
local thispid=$1
local fulllist=
local childlist=
childlist=$(ps --ppid $thispid -o pid h)
for pid in $childlist
  fulllist="$(pidlist $pid) $fulllist"
echo "$thispid $fulllist"

This outputs all the child, grandchild, etc. pids in space-delimited format. This can, in turn, be fed to ps, as in

ps -p $(pidlist *pid*)

I have writed this shell script based on @y_159 answer.

list_descendants ()
    local children=$( cat /proc/$1/task/*/children 2> /dev/null )
    for pid in $children
        list_descendants $pid
    echo $children

$ PID=`pidof gdm3`

$ ps f $PID $(list_descendants $PID)
   1492 ?        Ssl    0:00 /usr/sbin/gdm3
   1498 ?        Sl     0:00  \_ gdm-session-worker [pam/gdm-autologin]
   1531 tty2     Ssl+   0:00      \_ /usr/libexec/gdm-x-session --register-session ...
   1533 tty2     Sl+   27:25          \_ /usr/lib/xorg/Xorg vt2 -displayfd 3 ...
   1640 tty2     Sl+    0:00          \_ /usr/bin/startplasma-x11
   1745 ?        Ss     0:00              \_ /usr/bin/ssh-agent /usr/bin/

If you need even faster code then ...

$ awk -v PPID=`pidof gdm3` -v FPAT='[^ ]+|\\([^\\)]*\\)' '
    { a[$1] = $4 } END{ print PPID; desc(PPID) }
function desc( ppid,   i) {
    for ( i in a ) { if ( a[i] == ppid ) { print i; desc(i) }}
' /proc/[1-9]*/stat | xargs ps f
   1492 ?        Ssl    0:00 /usr/sbin/gdm3
   1498 ?        Sl     0:00  \_ gdm-session-worker [pam/gdm-autologin]
   1531 tty2     Ssl+   0:00      \_ /usr/libexec/gdm-x-session --register-session ...
   1533 tty2     Sl+   28:21          \_ /usr/lib/xorg/Xorg vt2 -displayfd 3 ...
   1640 tty2     Sl+    0:00          \_ /usr/bin/startplasma-x11
   1745 ?        Ss     0:00              \_ /usr/bin/ssh-agent /usr/bin/im-launch ...
  • for all processes: pstree -a
  • show by user: pstree user
  • 2
    Please explain some more why this answers the question. Mar 18 '16 at 7:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.