ps: Làm thế nào tôi có thể đệ quy tất cả quá trình con cho một pid nhất định


43

Làm cách nào tôi có thể lấy toàn bộ cây quy trình được sinh ra bởi một quy trình nhất định được hiển thị dưới dạng cây và chỉ cây đó tức là không có quy trình nào khác?

Đầu ra có thể trông giống như

 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

Tôi không thể có được kết quả mong muốn hoàn toàn với các tham số ps.

Sau đây cho kết quả mong muốn nhưng có vẻ hơi liên quan:

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

Có ai có một giải pháp dễ dàng hơn?


Không phải là một câu trả lời, nhưng bắt đầu với ps auxf.
jftuga

3
@jtfuga Đây thực tế là nơi tôi đã bắt đầu, nhưng điều này mang lại cho tôi tất cả các quy trình, đó chính xác là những gì tôi không muốn.
kynan

Câu trả lời:


38

Đây pstreelà một giải pháp rất tốt, nhưng nó có một chút kín đáo. Tôi sử dụng ps --forestthay thế. Nhưng không phải cho một PID( -p) vì nó chỉ in quy trình cụ thể, mà cho phiên ( -g). Nó có thể in ra bất kỳ thông tin nào pscó thể in trong cây nghệ thuật ASCII ưa thích xác định -otùy chọn.

Vì vậy, đề nghị của tôi cho vấn đề này:

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

Nếu quy trình không phải là một nhà lãnh đạo phiên, thì một mẹo nhỏ hơn phải được áp dụng:

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

Điều này nhận được id phiên (SID) của quy trình hiện tại trước và sau đó gọi lại ps với sid đó.

Nếu các tiêu đề cột không cần thiết, hãy thêm '=' sau mỗi định nghĩa cột trong các tùy chọn '-o', như:

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

Một ví dụ chạy và kết quả:

$ 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

Thật không may, điều này không hoạt động screenvì nó đặt sid cho mỗi màn hình con và tất cả bash cháu.

Để có được tất cả các quy trình được sinh ra bởi một quy trình, toàn bộ cây cần phải được xây dựng. Tôi đã sử dụng cho điều đó. Lúc đầu, nó xây dựng một mảng băm để chứa tất cả PID => ,child,child.... Cuối cùng, nó gọi một hàm đệ quy để trích xuất tất cả các tiến trình con của một tiến trình đã cho. Kết quả được truyền cho người khác psđể định dạng kết quả. PID thực tế phải được viết dưới dạng đối số cho thay vì <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)}')

Đối với quy trình SCREEN (pid = 8041), đầu ra ví dụ trông như thế này:

  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

Vấn đề với điều này (tôi biết, đó là một câu trả lời cũ) là tùy chọn --forest chỉ hoạt động trên Linux (hoặc các lệnh "ps" dựa trên gnu khác). Solaris và MacOS không thích nó.
Armand

@TrueY bạn có biết API C có thể làm điều đó không? Cảm ơn bạn
Bionix1441

Bionix Không, xin lỗi ... Tôi có thể đọc các thư mục / Proc / <PID> để xây dựng cây đó.
TrueY

1
Có vẻ như điều này sẽ đơn giản nếu pscó tùy chọn cờ bộ lọc chỉ để lọc cho tất cả các hậu duệ của một PID.
CMCDragonkai

27
pstree ${pid}

nơi mà ${pid}pid của quá trình cha mẹ.

Trên Gentoo Linux, pstreenằm trong gói "psmisc", dường như nằm ởhttp://psmisc.sourceforge.net/


9
Cảm ơn, nên đã đề cập rằng tôi đã xem pstree, nhưng đã bỏ lỡ một định dạng đầu ra dài dòng hơn. Tuy nhiên, pstree -p <pid>ít nhất là in các nắp đậy gần hợp lý.
kynan

2
Tôi cũng có vấn đề này, tôi cần phải thu thập tất cả các pids con một cách đệ quy nhưng tôi chỉ cần các pids, vì vậy tôi sẽ phải làm sedtất cả những điều đó .. mmm điều này hoạt động :)pstree -pn 4008 |grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*"
Aquarius Power

12

Đây là phiên bản của tôi chạy ngay lập tức (vì pschỉ được thực hiện một lần). Hoạt động trong bash và 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
Điều này cũng in ra pid của quy trình hiện tại, mà bạn có thể muốn hoặc không muốn. Tôi không muốn quy trình hiện tại được liệt kê (chỉ là hậu duệ của quy trình hiện tại), vì vậy tôi đã thay đổi tập lệnh bằng cách di chuyển echo $1bên trong vòng lặp đầu tiên và thay đổi nó thành echo $i.
Đánh dấu Lakata

1
Tôi chỉ định viết một hàm như vậy khi tôi tìm thấy cái này. Đây là giải pháp duy nhất có vẻ hoạt động trên MacOS, Linux và Solaris. Công việc tuyệt vời Tình yêu mà bạn dựa vào một lần chạy ps để hoàn thành công việc trong bộ nhớ.
Armand

3

Tôi đã tạo một tập lệnh bash nhỏ để tạo một danh sách các quy trình con của cha mẹ. Đệ quy cho đến khi nó tìm thấy quá trình con cuối cùng không có con. Nó không cung cấp cho bạn một cái nhìn cây. Nó chỉ liệt kê tất cả các 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

đối số đầu tiên của list_offspring là pid cha


1
Đã có một số câu trả lời khác đưa ra một tập lệnh bash không in một cây thực tế, bao gồm cả một câu hỏi trong chính câu hỏi. Câu trả lời (một phần) của bạn có lợi thế gì so với câu trả lời (một phần) hiện có?
David Richerby

Tôi đã sử dụng đệ quy và thêm một số giải thích. Nghĩ rằng nó có thể giúp ai đó. Nó làm chính xác những gì tiêu đề yêu cầu.
Merijn

Tôi thích việc sử dụng pgrep ở đây, vì ps có thể khác nhau giữa các biến thể unix.
John Szakmeister

3

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

không thêm một cây mỗi se, nó chỉ là danh sách các quy trình.

cho ví dụ

COMMAND
bash
  nvim
    python

Các -Htùy chọn được sử dụng để hiển thị một cây quá trình hoặc "quá trình phân cấp (rừng)"
Anthony G - công lý cho Monica

2

Tôi đã làm việc để tìm một giải pháp cho cùng một vấn đề. Về cơ bản, trang man ps không ghi lại bất kỳ tùy chọn nào cho phép thực hiện những gì chúng ta muốn chỉ bằng một lệnh. Kết luận: một kịch bản là cần thiết.

Tôi đã đưa ra một kịch bản rất giống với kịch bản của bạn. Tôi đã dán nó trong ~ / .bashrc để tôi có thể sử dụng nó từ bất kỳ shell nào.

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

Trên đường trên dòng lệnh:

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

nó xuất ra:

    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

Cách thanh lịch hơn theo cách đó là xác định hàm trong .bashrc:

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

sau đó trên dòng lệnh chạy:

subps bash

1

Điều này chỉ cung cấp cho pid + trẻ em mỗi cái trên một dòng rất hữu ích để xử lý thêm.

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

0

Tôi đã tạo một kịch bản tương tự dựa trên Philippe ở trên

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"
}

Điều này xuất ra tất cả các con, cháu, v.v. ở định dạng giới hạn không gian. Điều này có thể, đến lượt nó, được đưa đến ps, như trong

ps -p $ (pidlist pid )


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.