ps:我如何递归获取给定pid的所有子进程


43

我如何才能将给定进程生成的整个进程树显示为树,而只显示该树,即没有其他进程?

输出例如

 4378 ?        Ss     0:10 SCREEN
 4897 pts/16   Ss     0:00  \_ -/bin/bash
25667 pts/16   S+     0:00  |   \_ git diff
25669 pts/16   S+     0:00  |       \_ less -FRSX
11118 pts/32   Ss+    0:00  \_ -/bin/bash
11123 pts/32   S+     0:00      \_ vi

我仅凭参数不能获得所需的结果ps

以下给出了预期的结果,但似乎有点涉及:

#!/bin/bash

pidtree() {
  echo -n $1 " "
  for _child in $(ps -o pid --no-headers --ppid $1); do
    echo -n $_child `pidtree $_child` " "
  done
}

ps f `pidtree 4378`

有谁有更简单的解决方案?


不是答案,而是从开始ps auxf
jftuga

3
@jtfuga实际上这是我开始的地方,但这给了我所有的过程,而这正是我所不想要的。
kynan 2011年

Answers:


38

pstree是一个非常好的解决方案,但是有点沉默寡言。我ps --forest改用。但不是PID-p),因为它只打印特定的进程,但只打印会话(-g)。它可以打印出任何信息,ps可以在定义该-o选项的漂亮ASCII艺术树中打印。

所以我对这个问题的建议:

ps --forest -o pid,tty,stat,time,cmd -g 2795

如果该过程不是会话负责人,则必须应用更多技巧:

ps --forest -o pid,tty,stat,time,cmd -g $(ps -o sid= -p 2795)

这将首先获取当前进程的会话ID(SID),然后使用该sid再次调用ps。

如果不需要列标题,请在“ -o”选项的每个列定义后添加“ =”,例如:

ps --forest -o pid=,tty=,stat=,time=,cmd= -g $(ps -o sid= -p 2795)

运行示例和结果:

$ ps --forest -o pid=,tty=,stat=,time=,cmd= -g $(ps -o sid= -p 30085)
27950 pts/36   Ss   00:00:00 -bash
30085 pts/36   S+   00:00:00  \_ /bin/bash ./loop.sh
31888 pts/36   S+   00:00:00      \_ sleep 5

不幸的是,这不起作用,screen因为它为每个子屏幕和所有孙子bash设置了sid。

为了获得某个进程产生的所有进程,需要构建整棵树。我用。首先,它构建一个包含all的哈希数组PID => ,child,child...。最后,它调用递归函数以提取给定进程的所有子进程。结果将传递给另一个ps以格式化结果。实际的PID必须写为的参数,而不是<PID>

ps --forest $(ps -e --no-header -o pid,ppid|awk -vp=<PID> 'function r(s){print s;s=a[s];while(s){sub(",","",s);t=s;sub(",.*","",t);sub("[0-9]+","",s);r(t)}}{a[$2]=a[$2]","$1}END{r(p)}')

对于SCREEN进程(pid = 8041),示例输出如下所示:

  PID TTY      STAT   TIME COMMAND
 8041 ?        Ss     0:00 SCREEN
 8042 pts/8    Ss     0:00  \_ /bin/bash
 8092 pts/8    T      0:00      \_ vim test_arg test_server
12473 pts/8    T      0:00      \_ vim
12972 pts/8    T      0:00      \_ vim

这个问题(我知道,这是个老答案)是--forest选项仅适用于Linux(或其他基于gnu的“ ps”命令)。Solaris和MacOS不喜欢它。
阿曼德

@TrueY您知道可以做到这一点的C API吗?谢谢
Bionix1441

Bionix不,对不起...我可以阅读/ proc / <PID>目录来构建该树。
TrueY

1
如果ps具有过滤器标记选项只是为了过滤PID的所有后代,则这样的看起来应该很简单。
CMCDragonkai

27
pstree ${pid}

${pid}父进程的pid 在哪里。

在Gentoo Linux上,pstree软件包“ psmisc”位于以下位置:http://psmisc.sourceforge.net/


9
谢谢,应该提到我已经看过了pstree,但是错过了更详细的输出格式。但是,pstree -p <pid>至少要打印相当接近的pid。
kynan 2011年

2
我也有这个问题,我需要递归地收集所有子pid,但是我只需要这些pid,所以我将必须sed所有这些。.mmm 这项工作:)pstree -pn 4008 |grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*"
Aquarius Power

12

这是我的版本,可立即运行(因为ps仅执行一次)。适用于bash和zsh。

pidtree() (
    [ -n "$ZSH_VERSION"  ] && setopt shwordsplit
    declare -A CHILDS
    while read P PP;do
        CHILDS[$PP]+=" $P"
    done < <(ps -e -o pid= -o ppid=)

    walk() {
        echo $1
        for i in ${CHILDS[$1]};do
            walk $i
        done
    }

    for i in "$@";do
        walk $i
    done
)

1
这还会打印出您可能想要或不想要的当前进程的pid。我不想列出当前进程(只是当前进程的后代),所以我通过echo $1在第一个for循环内移动并将其更改为来更改了脚本echo $i
Mark Lakata

1
当我发现这个函数时,我正要编写这样的函数。这是唯一适用于MacOS,Linux和Solaris的解决方案。做得好。爱您依靠一次ps来完成内存中的工作。
Armand

3

我创建了一个小的bash脚本来创建父级子进程的列表pid。递归直到找到没有任何子进程的最后一个子进程。它没有给您树状视图。它仅列出所有pid。

function list_offspring {
  tp=`pgrep -P $1`          #get childs pids of parent pid
  for i in $tp; do          #loop through childs
    if [ -z $i ]; then      #check if empty list
      exit                  #if empty: exit
    else                    #else
      echo -n "$i "         #print childs pid
      list_offspring $i     #call list_offspring again with child pid as the parent
    fi;
  done
}
list_offspring $1

list_offspring的第一个参数是父pid


1
已经有其他几个答案给出了一个不打印实际树的bash脚本,包括问题本身中的一个。与现有的(部分)答案相比,您的(部分)答案具有哪些优势?
David Richerby 2015年

我使用了递归并添加了一些解释。以为这可能对某人有帮助。它确实满足标题的要求。
Merijn 2015年

我喜欢在这里使用pgrep,因为ps在unix变体之间可能有所不同。
John Szakmeister '16

3

ps -H -g "$pid" -o comm

本身并不会添加树,而只是进程列表。

例如

COMMAND
bash
  nvim
    python

-H选项用于显示过程树或“过程层次结构(森林)”
Anthony G-Monica的正义

2

我一直在努力寻找完全相同的问题的解决方案。基本上,ps联机帮助页中没有记录任何允许使用单个命令执行所需操作的选项。结论:需要一个脚本。

我想出了一个与您的脚本非常相似的脚本。我将其粘贴到〜/ .bashrc中,以便可以从任何shell中使用它。

pidtree() {
  local parent=$1
  local list=
  while [ "$parent" ] ; do     
    if [ -n "$list" ] ; then
      list="$list,$parent"
    else
      list="$parent"
    fi
    parent=$(ps --ppid $parent -o pid h)
  done
  ps -f -p $list f
}

1

在命令行上:

ps -o time,pid,ppid,cmd --forest -g -p $(pgrep -x bash)

它输出:

    TIME   PID  PPID CMD
00:00:00  5484  5480 bash
00:00:01  5531  5484  \_ emacs -nw .bashrc
00:00:01  2986  2984 /bin/bash
00:00:00  4731  2986  \_ redshift
00:00:00  5543  2986  \_ ps -o time,pid,ppid,cmd --forest -g -p 2986 5484

这种方式的更优雅的方式是在中定义一个函数.bashrc

function subps()                                                                                    
{                                                                                                   
    process=$(pgrep -x $1)                                                                                                                                                                     
    ps -o time,pid,ppid,cmd --forest -g -p $process                                                 
}    

然后在命令行上运行:

subps bash

1

这仅使pid + childs pids分别在一行上,这对于进一步处理很有用。

pidtree() {
    for _pid in "$@"; do
        echo $_pid
        pidtree `ps --ppid $_pid -o pid h`
    done
}

0

我根据Philippe的上述内容制作了类似的脚本

pidlist() {
local thispid=$1
local fulllist=
local childlist=
childlist=$(ps --ppid $thispid -o pid h)
for pid in $childlist
do
  fulllist="$(pidlist $pid) $fulllist"
done
echo "$thispid $fulllist"
}

这将以空格分隔的格式输出所有子代,孙代等pid。可以依次将其馈送到ps,如

ps -p $(pidlist pid


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.