ps: Bagaimana saya bisa secara rekursif mendapatkan semua proses anak untuk pid yang diberikan

43

Bagaimana saya bisa mendapatkan seluruh pohon proses menelurkan oleh proses yang diberikan ditampilkan sebagai pohon dan hanya pohon itu yaitu tidak ada proses lain?

Outputnya misalnya bisa seperti

 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

Saya tidak bisa mendapatkan hasil yang diinginkan murni dengan parameter ps.

Berikut ini memberikan hasil yang diinginkan tetapi tampaknya sedikit terlibat:

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

Adakah yang punya solusi yang lebih mudah?

kynan
sumber
Bukan jawaban, tetapi mulai dengan ps auxf.
jftuga
3
@ jtfuga Ini sebenarnya tempat saya memulai, tapi ini memberi saya semua proses, yang sebenarnya tidak saya inginkan.
kynan

Jawaban:

38

Ini pstreeadalah solusi yang sangat bagus, tetapi agak pendiam. Saya menggunakan ps --forestsebagai gantinya. Tetapi tidak untuk a PID( -p) karena hanya mencetak proses spesifik, tetapi untuk sesi ( -g). Ia dapat mencetak informasi apa pun yang psdapat dicetak dalam pohon seni ASCII yang mewah yang menentukan -oopsi.

Jadi saran saya untuk masalah ini:

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

Jika prosesnya bukan pemimpin sesi, maka sedikit trik yang harus diterapkan:

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

Ini mendapatkan id sesi (SID) dari proses saat ini terlebih dahulu dan kemudian memanggil ps lagi dengan sisi itu.

Jika tajuk kolom tidak diperlukan, tambahkan '=' setelah setiap definisi kolom dalam opsi '-o', seperti:

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

Contoh dijalankan dan hasilnya:

$ 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

Sayangnya ini tidak berfungsi karena screenmengatur sid untuk setiap layar anak dan semua cucu bash.

Untuk mendapatkan semua proses yang dihasilkan oleh suatu proses, seluruh pohon perlu dibangun. Saya menggunakan untuk itu. Pada awalnya ia membangun array hash untuk mengandung semua PID => ,child,child.... Pada akhirnya ia memanggil fungsi rekursif untuk mengekstrak semua proses anak dari proses yang diberikan. Hasilnya diteruskan ke yang lain psuntuk memformat hasilnya. PID yang sebenarnya harus ditulis sebagai argumen untuk alih-alih <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)}')

Untuk proses SCREEN (pid = 8041) contoh output terlihat seperti ini:

  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
Benar
sumber
Masalah dengan ini (saya tahu, ini adalah jawaban lama) adalah - opsi hutan hanya bekerja di Linux (atau perintah "ps" berbasis gnu lainnya). Solaris, dan MacOS tidak menyukainya.
Armand
@ Benar apakah Anda tahu C API yang bisa melakukan itu? Terima kasih
Bionix1441
Bionix Tidak, maaf ... Saya dapat membaca direktori / proc / <PID> untuk membangun pohon itu.
Benar,
1
Sepertinya ini harus sederhana jika psmemiliki opsi bendera filter hanya untuk menyaring semua keturunan PID.
CMCDragonkai
27
pstree ${pid}

dimana ${pid}pid dari proses induk.

Di Gentoo Linux, pstreeada dalam paket "psmisc," tampaknya terletak dihttp://psmisc.sourceforge.net/

Eroen
sumber
9
Terima kasih, seharusnya menyebutkan bahwa saya telah melihat pstree, tetapi melewatkan format output yang lebih verbose. Namun, pstree -p <pid>setidaknya cetak pids yang cukup dekat.
kynan
2
Saya punya masalah ini juga, saya perlu mengumpulkan semua anak secara rekursif tapi saya hanya perlu, jadi saya harus sedsemua itu .. mmm ini bekerja :)pstree -pn 4008 |grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*"
Aquarius Power
12

Berikut ini adalah versi saya yang berjalan secara instan (karena psdieksekusi hanya sekali). Bekerja di bash dan 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
)
xzfc
sumber
1
Ini juga mencetak pid dari proses saat ini, yang Anda mungkin atau mungkin tidak mau. Saya tidak ingin proses saat ini terdaftar (hanya keturunan dari proses saat ini), jadi saya mengubah skrip dengan bergerak echo $1di dalam pertama untuk loop dan mengubahnya ke echo $i.
Mark Lakata
1
Saya baru saja akan menulis fungsi seperti itu ketika saya menemukan ini. Ini adalah satu-satunya solusi yang tampaknya berfungsi pada MacOS, Linux, dan Solaris. Kerja bagus. Senang bahwa Anda bergantung pada satu jalankan ps untuk mendapatkan pekerjaan yang dilakukan dalam memori.
Armand
3

Saya telah membuat skrip bash kecil untuk membuat daftar pid tentang proses anak orang tua. Secara rekursif sampai menemukan proses anak terakhir yang tidak memiliki anak. Itu tidak memberi Anda tampilan pohon. Itu hanya daftar semua 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

Argumen pertama dari list_offspring adalah parent parent

Merijn
sumber
1
Sudah ada beberapa jawaban lain yang memberikan skrip bash yang tidak mencetak pohon sebenarnya, termasuk satu di pertanyaan itu sendiri. Apa keuntungan yang dimiliki jawaban Anda (sebagian) dari jawaban (sebagian) yang ada
David Richerby
Saya menggunakan rekursi dan menambahkan beberapa penjelasan. Kupikir itu mungkin membantu seseorang. Ia melakukan persis seperti yang diminta judulnya.
Merijn
Saya suka penggunaan pgrep di sini, karena ps dapat berbeda di antara varian unix.
John Szakmeister
3

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

tidak menambahkan pohon per se, itu hanya daftar proses.

memberi misalnya

COMMAND
bash
  nvim
    python
edi9999
sumber
The -Hopsi digunakan untuk menampilkan pohon proses atau "hirarki proses (hutan)"
Anthony G - keadilan bagi Monica
2

Saya telah bekerja untuk menemukan solusi untuk masalah yang sama persis. Pada dasarnya, ps manpage tidak mendokumentasikan opsi apa pun yang memungkinkan untuk melakukan apa yang kita inginkan dengan satu perintah. Kesimpulan: naskah dibutuhkan.

Saya datang dengan skrip yang sangat mirip dengan Anda. Saya menempelkannya di ~ / .bashrc saya jadi saya bisa menggunakannya dari shell apa saja.

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
}
Philippe A.
sumber
1

Dalam perjalanan di command-line:

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

itu output:

    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

cara yang lebih elegan dari cara itu adalah mendefinisikan suatu fungsi di .bashrc:

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

kemudian pada menjalankan baris perintah:

subps bash
Shakiba Moshiri
sumber
1

Ini hanya memberikan pid + anak-anak masing-masing pada satu baris yang berguna untuk diproses lebih lanjut.

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

Saya membuat skrip serupa berdasarkan Philippe di atas

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

Ini akan menampilkan semua anak, cucu, dll. Dalam format terbatas-ruang. Pada gilirannya, ini dapat diumpankan ke ps, seperti pada

ps -p $ (pidlist pid )

kylehutson
sumber
-2

untuk semua proses - pstree -sebuah pertunjukan oleh pengguna - pengguna pstree

Andy Wong
sumber
2
Tolong jelaskan lebih lanjut mengapa ini menjawab pertanyaan.
pengguna 99572 baik-baik saja