Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I find the top-level parent PID of a given process using bash?

Tags:

Let's say I run ps axf and I can see that my command's process tree looks like this:

  800 ?        Ss     0:00 /usr/sbin/sshd
10186 ?        Ss     0:00  \_ sshd: yukondude [priv]
10251 ?        S      0:00      \_ sshd: yukondude@pts/0
10252 pts/0    Ss     0:00          \_ -bash
10778 pts/0    S      0:00              \_ su -
10785 pts/0    S      0:00                  \_ -su
11945 pts/0    R+     0:00                      \_ ps axf

I know I can check $$ for the current shell's PID (10785) or $PPID for the parent PID (10778).

But I just want the top-level parent PID, which would be 800 (SSH daemon) in this example. Is there any way to do that easily?

I learned from this SO answer that I can recursively check the 4th entry in the /proc/PID/stat file to find each process's parent PID:

# cut -f4 -d' ' /proc/10785/stat
10778
# cut -f4 -d' ' /proc/10778/stat
10252
# cut -f4 -d' ' /proc/10252/stat
10251
# cut -f4 -d' ' /proc/10251/stat
10186
# cut -f4 -d' ' /proc/10186/stat
800
# cut -f4 -d' ' /proc/800/stat
1

(The top-level parent PID will be the one just before I reach init's PID, i.e., 1.)

Before I write a little loop (I'm not even sure if you can use recursion in bash) to do this, is there a much more straightforward method that I'm missing? Maybe just another parameter of a file under /proc? A grep through those files didn't reveal anything obvious.

Edit: Of course, the top-level process for all Linux processes is /sbin/init with a PID of 1. What I want is the PID of the parent just before that: the penultimate parent.

like image 332
yukondude Avatar asked Aug 27 '10 17:08

yukondude


1 Answers

Bash can definitely do recursion.

You can retrieve the fourth field from the stat file without using the external cut utility by doing something like this:

stat=($(</proc/$$/stat))    # create an array
ppid=${stat[3]}             # get the fourth field

If the command might have space(s) in its name, you can count from the end of the array (assuming that the number of fields is stable). This will also work if there are no spaces in the command's name.

ppid=${stat[-49]}           # gets the same field but counts from the end

Here's another technique which should avoid those problems (but may fail if the command name contains a newline):

mapfile -t stat < /proc/$$/status
ppid=${stat[5]##*$'\t'}

The fifth field in that file looks like:

PPid:    1234

and the brace expansion strips the everything up to the tab character leaving just the numeric part.

like image 51
Dennis Williamson Avatar answered Sep 22 '22 20:09

Dennis Williamson