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

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.

19
задан Community 23 May 2017 в 12:18
поделиться

2 ответа

Если нет лучшего решения, вот простой (рекурсивный) скрипт для получения родительского PID верхнего уровня любого заданного вами номера процесса (или текущей оболочки, если вы опустите Аргумент PID):

#!/bin/bash
# Look up the top-level parent Process ID (PID) of the given PID, or the current
# process if unspecified.

function top_level_parent_pid {
    # Look up the parent of the given PID.
    pid=${1:-$$}
    stat=($(</proc/${pid}/stat))
    ppid=${stat[3]}

    # /sbin/init always has a PID of 1, so if you reach that, the current PID is
    # the top-level parent. Otherwise, keep looking.
    if [[ ${ppid} -eq 1 ]] ; then
        echo ${pid}
    else
        top_level_parent_pid ${ppid}
    fi
}

Просто используйте этот скрипт и вызовите top_level_parent_pid с аргументом PID или без него, в зависимости от ситуации.

Спасибо @Dennis Williamson за его многочисленные предложения о том, как написать этот сценарий компактно и эффективно.

10
ответ дан 30 November 2019 в 04:20
поделиться

Bash определенно может выполнять рекурсию.

Вы можете получить четвертое поле из файла статистики без использования внешней утилиты cut, выполнив примерно следующее:

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

Если в имени команды есть пробелы, вы можете подсчитать от конца массива (при условии, что количество полей стабильно). Это также будет работать, если в имени команды нет пробелов.

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

Вот еще один метод, который должен избежать этих проблем (но может дать сбой, если имя команды содержит новую строку):

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

Пятое поле в этом файле выглядит так:

PPid:    1234

и раскрытие фигурной скобки удаляет все до табуляции символ, оставляя только числовую часть.

11
ответ дан 30 November 2019 в 04:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: