如何使用 bash 找到给定进程的顶级父 PID?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3586888/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 19:37:29  来源:igfitidea点击:

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

bashprocess

提问by yukondude

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

假设我运行ps axf,我可以看到我的命令的进程树如下所示:

  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 $PPIDfor the parent PID (10778).

我知道我可以检查$$当前 shell 的 PID (10785) 或$PPID父 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?

但我只想要顶级父 PID,在本例中为 800(SSH 守护进程)。有没有办法轻松做到这一点?

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

我从这个 SO answer中了解到,我可以递归检查/proc/PID/stat文件中的第 4 个条目以找到每个进程的父 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.)

(顶级父 PID 将是我到达initPID之前的那个,即 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 grepthrough those files didn't reveal anything obvious.

在我编写一个小循环(我什至不确定您是否可以在 bash 中使用递归)来执行此操作之前,是否有我缺少的更直接的方法?也许只是下一个文件的另一个参数/proc?一grep,通过这些文件并没有透露任何明显。

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.

编辑:当然,所有 Linux 进程的顶级进程是 /sbin/init,PID 为 1。我想要的是之前的父进程的 PID:倒数第二个父进程。

采纳答案by yukondude

Failing a better solution, here's a simple (recursive) script to get the top-level parent PID of any process number you give it (or the current shell if you leave out the PID argument):

如果没有更好的解决方案,这里有一个简单的(递归)脚本,用于获取您提供的任何进程号的顶级父 PID(如果省略 PID 参数,则为当前 shell):

#!/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
}

Just sourcethis script and call top_level_parent_pidwith or without a PID argument, as appropriate.

source此脚本并根据需要top_level_parent_pid使用或不使用 PID 参数调用。

Thanks to @Dennis Williamson for his many suggestions on how to write this script compactly and efficiently.

感谢@Dennis Williamson 就如何简洁高效地编写此脚本提出了许多建议。

回答by Paused until further notice.

Bash can definitely do recursion.

Bash 绝对可以进行递归。

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

您可以cut通过执行以下操作,在不使用外部实用程序的情况下从 stat 文件中检索第四个字段:

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 fieldsis 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.

大括号扩展将所有内容删除到制表符,只留下数字部分。

回答by Albert

Another solution (from here):

另一个解决方案(从这里):

ps -p $$ -o ppid=

回答by Wis

I improved upon your (@yukondude)'s recursive solution to avoid issues where the command name contains internal field separator (IFS) characters like space, tab, and newline, which are legal Unix filename characters.

我改进了您 (@yukondude) 的递归解决方案,以避免出现命令名称包含内部字段分隔符 (IFS) 字符(如空格、制表符和换行符)的问题,这些字符是合法的 Unix 文件名字符。

#!/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:-$$}
    ppid="$(awk '/^PPid:/ { print  }' < /proc/"$pid"/status)"
    # /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
}

回答by mikemaccana

OS X version, adapted from @albert and @yukondude's answers:

OS X 版本,改编自 @albert 和 @yukondude 的回答:

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

# From http://stackoverflow.com/questions/3586888/how-do-i-find-the-top-level-parent-pid-of-a-given-process-using-bash
function top_level_parent_pid {
    # Look up the parent of the given PID.
    PID=${1:-$$}
    PARENT=$(ps -p $PID -o ppid=)

    # /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 [[ ${PARENT} -eq 1 ]] ; then
        echo ${PID}
    else
        top_level_parent_pid ${PARENT}
    fi
}

回答by bishop

Iterative version:

迭代版本:

# ppid -- Show parent PID
#  - The process whose parent you want to show, default to $$
function ppid() {
    local stat=($(</proc/${1:-$$}/stat))
    echo ${stat[3]}
}

# apid -- Show all ancestor PID
#  - The process whose ancestors you want to show, default to $$
#  - Stop when you reach this ancestor PID, default to 1
function apid() {
    local ppid=$(ppid ${1:$$})
    while [ 0 -lt $ppid -a ${2:-1} -ne $ppid ]; do
        echo $ppid
        ppid=$(ppid $ppid)
    done
}

As two separate functions, because sometimes you want parent PID only, and sometimes you want the whole tree.

作为两个独立的函数,因为有时您只需要父 PID,有时您需要整个树。