I would like to create a bash alias that gives me the process tree from the current bash session I am using, up to init.
The use case is to know whether I have used bash
or vi
's :shell
command.
I am using MacOS X. I have heard about pstree
, but it seems to only show children, not the relationship between init and the current process.
I am sure with a a bit of google search, you can find how to get and download pstree
for the Mac. However, you can do a poor man's version, using ps
and ppid
.
eg
ps -eo ppid,pid,cmd | awk '{p[$1]=p[$1]","$3}END{ for(i in p) print i, p[i]}'
This is supported in pstree(1)
by using an option to show the tree only for a particular PID and providing the current process's PID ($$
in Bash), The option is named differently between GPL-licensed version by Werner Almesberger distributed with Debian and the BSD version by Fred Hucht distributed with MacOS.
On Debian/Ubuntu: pstree -s $$
init───gnome-terminal───bash───pstree
Summary of -s
option:
-s Show parent processes of the specified process.
On MacOS: pstree -p $$
-+= 00001 root /sbin/launchd
\-+= 25706philipbranning/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal
\-+= 25716 root login -pfl philipbranning /bin/bash -c exec -la bash /bin/bash
\-+= 25722 philipbranning -bash
\-+= 32456 philipbranning pstree -p 25722
\--- 32457 root ps -axwwo user,pid,ppid,pgid,command
Summary of -p
option:
-p pid show only branches containing process <pid>
Here's your alias for MacOS:
alias psme='pstree -p $$'
I used Mac OS 10.7 Lion, but I think this will be fairly portable to Bourne-like shells on other Unix-like systems. You may have issues with the command keyword in the argument to ps.
I put the following code in a file named procsup.sh, which defines a shell function to follow the process's parents up to process ID 1. (I often find shell functions easier to work with than aliases.)
procsup()
{
leaf=$$
ps -eo pid,ppid,command | awk -v leaf="$leaf" \
'{parent[$1]=$2;command[$1]=$3;}
function print_ancestry(pid)
{
print pid " (" command[pid] ") child of " parent[pid];
if(pid!=1) print_ancestry(parent[pid])
};
END{\
print_ancestry(leaf)
}'
}
Then I started a shell and sourced procsup.sh. In real life you would ensure that your new shells would automatically source procsup.sh when started, maybe in your personal .bashrc. First I checked the ancestry from that shell. Then I started vi from that shell. As usual the interaction with vi didn't make it to the transcript until I did :shell
. My terminal window looked like this:
Mariel:~/Library/Scripts 1j david$
Mariel:~/Library/Scripts 1j david$
Mariel:~/Library/Scripts 1j david$ . procsup.sh
Mariel:~/Library/Scripts 1j david$ procsup
41926 (-bash) child of 41922
41922 (login) child of 41917
41917 (/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal) child of 19281
19281 (/sbin/launchd) child of 1
1 (/sbin/launchd) child of 0
Mariel:~/Library/Scripts 1j david$
Mariel:~/Library/Scripts 1j david$
Mariel:~/Library/Scripts 1j david$ vi
bash-3.2$ # Have just done :shell.
bash-3.2$ . procsup.sh
bash-3.2$ procsup
42325 (/bin/bash) child of 42324
42324 (vi) child of 41926
41926 (-bash) child of 41922
41922 (login) child of 41917
41917 (/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal) child of 19281
19281 (/sbin/launchd) child of 1
1 (/sbin/launchd) child of 0
bash-3.2$
bash-3.2$
If you use a package manager like MacPorts you can easily install pstree.
I don't have the whole answer that you're looking for, but I've got an idea that might move you in the right direction. The command
declare -A parent
will create an associative array (a hash, if you speak Perl)
You will need some command that will give you name-value pairs for PID and PPID... my guess is that the mac's ps command can be made to do this if you torture it enough. I'm going to use 'ps -eo' as above, but you'll want to fill in the blanks.
Then you can do something like this:
ps -eo pid,ppid | while read pid ppid
do
parent[$pid]=$ppid
echo "pid: $pid ppid: ${parent[$pid]} grandppid: ${parent[${parent[$pid]}]}"
done
I was having trouble making the values of $parent persist outside of my while loop, otherwise I would have created a second for loop to traverse from $$ back to init. I'll leave that as an exercise to the reader.