ps: How can i recursively get all child process for a given pid

122,068

Solution 1

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:

  PID TTY      STAT   TIME COMMAND
 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

Solution 2

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/

Solution 3

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
        done
    }

    for i in "$@";do
        walk $i
    done
)

Solution 4

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

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

gives for example

COMMAND
bash
  nvim
    python

Solution 5

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
    fi;
  done
}
list_offspring $1

first argument of list_offspring is the parent pid

Share:
122,068

Related videos on Youtube

kynan
Author by

kynan

Updated on September 18, 2022

Comments

  • kynan
    kynan over 1 year

    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:

    #!/bin/bash
    
    pidtree() {
      echo -n $1 " "
      for _child in $(ps -o pid --no-headers --ppid $1); do
        echo -n $_child `pidtree $_child` " "
      done
    }
    
    ps f `pidtree 4378`
    

    Does anyone have an easier solution?

    • jftuga
      jftuga over 12 years
      Not an answer, but start with ps auxf.
    • kynan
      kynan over 12 years
      @jtfuga This is in fact where I started, but this gives me all processes, which is exactly what I don't want.
  • kynan
    kynan over 12 years
    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.
  • Aquarius Power
    Aquarius Power almost 10 years
    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:]]*"
  • David Richerby
    David Richerby almost 9 years
    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?
  • Merijn
    Merijn almost 9 years
    I used recursion and added some explanation. Thought it might help someone. It does exactly what the title asks for.
  • user 99572 is fine
    user 99572 is fine about 8 years
    Please explain some more why this answers the question.
  • John Szakmeister
    John Szakmeister almost 8 years
    I like the use of pgrep here, since ps can differ among unix variants.
  • Mark Lakata
    Mark Lakata over 7 years
    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.
  • Anthony Geoghegan
    Anthony Geoghegan over 7 years
    The -H option is used to display a process tree or "process hierarchy (forest)"
  • Armand
    Armand almost 7 years
    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
    Armand almost 7 years
    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.
  • Bionix1441
    Bionix1441 over 6 years
    @TrueY do you know of a C API that can do that ? Thank you
  • TrueY
    TrueY over 6 years
    Bionix No, sorry... I may read /proc/<PID> directories to build that tree.
  • CMCDragonkai
    CMCDragonkai over 5 years
    Seems like this should be simple if ps had a filter flag option just to filter to all descendants of a PID.
  • Irfan Latif
    Irfan Latif about 4 years
    Simple and splendid.
  • x-yuri
    x-yuri about 4 years
    Regarding your last awk oneliner, I wonder if it has to be that complex?
  • Matiullah Khan
    Matiullah Khan about 3 years
    I like ps -fH -g <pid> as an quick approximation to your answer :)
  • Admin
    Admin almost 2 years
    Thanks you for the inspiration. I made a POSIX compliant version of it: superuser.com/a/1721354/114255