代码之家  ›  专栏  ›  技术社区  ›  yukondude

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

  •  17
  • yukondude  · 技术社区  · 14 年前

    假设我跑了 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
    

    我知道我能查到 $$ $PPID 对于父PID(10778)。

    但我只需要顶级父PID,在本例中是800(SSH守护进程)。有没有什么方法可以轻松做到这一点?

    我从中学到了 this SO answer /proc/PID/stat 文件以查找每个进程的父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
    

    init 的PID,即1。)

    /proc ? A grep 通过这些文件没有发现任何明显的迹象。

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

    5 回复  |  直到 8 年前
        1
  •  12
  •   Dennis Williamson    6 年前

    Bash绝对可以做递归。

    您可以从stat文件中检索第四个字段,而无需使用外部 cut 通过这样做:

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

    如果命令名中可能有空格,则可以从数组末尾开始计数(假设 number of fields 是稳定的)。如果命令名中没有空格,也可以这样做。

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

    下面是另一种可以避免这些问题的技术(但如果命令名包含换行符,则可能会失败):

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

    该文件中的第五个字段如下所示:

    PPid:    1234
    

    大括号展开将所有内容剥离到tab字符,只留下数字部分。

        2
  •  10
  •   pix0r    14 年前

    如果没有更好的解决方案,下面是一个简单的(递归)脚本,用于获取给定的任何进程号的顶级父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
    }
    

    source 这个脚本和调用 top_level_parent_pid 带或不带PID参数(视情况而定)。

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

        3
  •  5
  •   Community CDub    8 年前

    另一个解决方案(来自 here

    ps -p $$ -o ppid=
    
        4
  •  1
  •   bishop    9 年前

    迭代版本:

    # ppid -- Show parent PID
    # $1 - 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
    # $1 - The process whose ancestors you want to show, default to $$
    # $2 - 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
    }
    

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

        5
  •  1
  •   Wis    6 年前

    #!/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 $2 }' < /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
    }
    
        6
  •  0
  •   mikemaccana    9 年前

    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
    }