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 \_ viI 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?
215 Answers
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 2795If 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 5Unfortunately 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 awk 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 awk 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 6 pstree ${pid}where ${pid} is the pid of the parent process.
On Gentoo Linux, pstree is in the package "psmisc," apparently located at
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
) 3 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 $1first argument of list_offspring is the parent pid
3ps -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 1 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 list="$list,$parent" else list="$parent" fi parent=$(ps --ppid $parent -o pid h) done 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` done
}With nicer tree layout:
_pidtree() { local p="$1" shift for _pid in "$@"; do echo "$p$_pid" _pidtree " $p" `ps --ppid $_pid -o pid h` done
}
pidtree() { _pidtree '' "$@"
} 1 On way on the command-line:
ps -o time,pid,ppid,cmd --forest -g -p $(pgrep -x bash)it outputs:
TIME PID PPID CMD
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 5484more 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
@xzfc's solution () works. However it uses some shell-specific features (e.g., shopt, associative array). It also has special handling for zsh.
I adapted it to make it POSIX compliant:
pidtree() ( # lines of "parent child" _PID_GRAPH="$(ps -e -o ppid= -o pid=)" _walk() { echo $1 # iterate direct children of "$1" # awk: if the parent field matches $1, then print child field echo "$_PID_GRAPH" | awk -F' ' "\$1==\"$1\" {print \$2;}" | while read child_pid; do _walk "$child_pid" done } for i in "$@"; do _walk $i; done
) 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 done
}
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
do fulllist="$(pidlist $pid) $fulllist"
done
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 do list_descendants $pid done echo $children
}
$ PID=`pidof gdm3`
$ ps f $PID $(list_descendants $PID) PID TTY STAT TIME COMMAND 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 PID TTY STAT TIME COMMAND 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 ... If we want to see all the children and all subchildren of a process the first answer doesnt work. The first answer assumes that all children and subchildren have the same session id. It is wrong.
An example: lets take pid=17601
$ ps --forest -o pid,ppid,pgrp,sess,tty,stat,time,cmd -g $(ps -o sid= -p 17601) PID PPID PGRP SESS TT STAT TIME CMD
17601 1109 17601 17601 ? Ss 00:00:00 sshd: vasya [priv]
17652 17601 17601 17601 ? S 00:00:00 \_ sshd: vasya@pts/17so we take the process with pid=17601, find its session id(17601) and list all the processes with the session id. For pid=17601 we get only one child.
However the real process subtree looks like this:
$ pstree -A -s -p 17601
systemd(1)---sshd(1109)---sshd(17601)---sshd(17652)---bash(17653)
$ ps -o user,pid,ppid,pgrp,sess,stat,cmd -p 1,1109,17601,17652,17653 --forest
USER PID PPID PGRP SESS STAT CMD
root 1 0 1 1 Ss /sbin/init splash
root 1109 1 1109 1109 Ss /usr/sbin/sshd -D
root 17601 1109 17601 17601 Ss \_ sshd: vasya [priv]
vasya 17652 17601 17601 17601 S \_ sshd: vasya@pts/17
vasya 17653 17652 17653 17653 Ss+ \_ -bashAs you can see the pid=17601 has one child and one subchild (pid=17652,17653).The point is we have found the subchild PID=17653. The method from the first answer doesn't do that. This method has disadvantage because it uses two steps. However the advantage is that is we specify a PID then we get not only all the children and all subchildren but also all the parents. That is we get all the tree for the PID. We get the whole tree. All the processes in the list are either a parent or a child for a neighbour.
- for all processes:
pstree -a - show by user:
pstree user