ps: Wie kann ich rekursiv alle untergeordneten Prozesse für eine bestimmte pid erhalten

43

Wie kann ich den gesamten Prozessbaum, der von einem bestimmten Prozess erzeugt wurde, als Baum anzeigen lassen und nur diesen Baum, dh keine anderen Prozesse?

Die Ausgabe könnte zB so aussehen

 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

Ich konnte nicht das gewünschte Ergebnis rein mit Parametern erzielen ps.

Das Folgende gibt das gewünschte Ergebnis, scheint aber ein bisschen beteiligt zu sein:

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

Hat jemand eine einfachere Lösung?

kynan
quelle
Keine Antwort, aber fange an mit ps auxf.
Jftuga
3
@jtfuga Das ist in der Tat, wo ich angefangen habe, aber das gibt mir alle Prozesse, was genau das ist, was ich nicht will.
kynan

Antworten:

38

Das pstreeist eine sehr gute Lösung, aber es ist ein bisschen zurückhaltend. Ich benutze ps --foreststattdessen. Aber nicht für a PID( -p), weil es nur den spezifischen Prozess ausgibt, sondern für die session ( -g). Es kann alle Informationen psausdrucken, die in einem ausgefallenen ASCII-Kunstbaum gedruckt werden können, der die -oOption definiert.

Also mein Vorschlag für dieses Problem:

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

Wenn der Prozess kein Sitzungsleiter ist, muss ein kleiner Trick angewendet werden:

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

Dies ruft zuerst die Sitzungs-ID (SID) des aktuellen Prozesses ab und ruft dann mit dieser Seite erneut ps auf.

Wenn die Spaltenüberschriften nicht benötigt werden, fügen Sie nach jeder Spaltendefinition ein '=' in die Optionen '-o' ein, wie zum Beispiel:

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

Ein Beispiellauf und das Ergebnis:

$ 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

Leider funktioniert dies nicht, screenda es die Seite für jeden untergeordneten Bildschirm und alle Enkelkind-Bash festlegt.

Damit alle Prozesse von einem Prozess erzeugt werden, muss der gesamte Baum erstellt werden. Ich habe dafür benutzt. Zunächst wird ein Hash-Array erstellt, das alle Elemente enthält PID => ,child,child.... Am Ende wird eine rekursive Funktion aufgerufen, um alle untergeordneten Prozesse eines bestimmten Prozesses zu extrahieren. Das Ergebnis wird an einen anderen übergeben, psum das Ergebnis zu formatieren. Die tatsächliche PID muss als Argument für anstatt <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)}')

Für einen SCREEN-Prozess (pid = 8041) sieht die Beispielausgabe folgendermaßen aus:

  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
TrueY
quelle
Das Problem dabei (ich weiß, es ist eine alte Antwort) ist, dass die Option --forest nur unter Linux (oder anderen gnu-basierten "ps" -Befehlen) funktioniert. Solaris und MacOS mögen das nicht.
Armand
@TrueY kennen Sie eine C-API, die das kann? Vielen Dank
Bionix1441
Bionix Nein, tut mir leid ... Ich kann / proc / <PID> -Verzeichnisse lesen, um diesen Baum zu erstellen.
TrueY
1
Scheint so einfach zu sein, wenn pseine Filter-Flag-Option vorhanden wäre, um nur nach allen Nachkommen einer PID zu filtern.
CMCDragonkai
27
pstree ${pid}

Wo ${pid}ist die PID des übergeordneten Prozesses.

Unter Gentoo Linux pstreebefindet sich im Paket "psmisc" anscheinend unterhttp://psmisc.sourceforge.net/

Eroen
quelle
9
Danke, hätte erwähnen sollen, dass ich mir pstreeein ausführlicheres Ausgabeformat angeschaut habe , aber verpasst habe. pstree -p <pid>Drucken Sie jedoch mindestens die Pids aus, die ziemlich nah sind.
kynan
2
Ich habe auch dieses Problem, ich muss alle Kinder-Pids rekursiv sammeln, aber ich brauche nur die Pids, also müsste ich das sedalles .. mmm das funktioniert :)pstree -pn 4008 |grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*"
Aquarius Power
12

Hier ist meine Version, die sofort ausgeführt wird (da psnur einmal ausgeführt). Funktioniert in bash und 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
quelle
1
Dies druckt auch die PID des aktuellen Prozesses aus, die Sie möglicherweise oder möglicherweise nicht möchten. Ich wollte nicht, dass der aktuelle Prozess aufgelistet wird (nur Nachkommen des aktuellen Prozesses), also habe ich das Skript geändert, indem ich echo $1in die erste for-Schleife verschoben und in geändert habe echo $i.
Mark Lakata
1
Ich wollte gerade eine solche Funktion schreiben, als ich diese fand. Dies ist die einzige Lösung, die unter MacOS, Linux und Solaris zu funktionieren scheint. Gute Arbeit. Ich liebe es, dass Sie sich auf einen einzigen Lauf von ps verlassen, um die Arbeit in Erinnerung zu behalten.
Armand
3

Ich habe ein kleines Bash-Skript erstellt, um eine Liste mit Pids der untergeordneten Prozesse eines Elternteils zu erstellen. Rekursiv, bis der letzte untergeordnete Prozess gefunden wird, der keine untergeordneten Prozesse enthält. Es gibt Ihnen keine Baumansicht. Es werden nur alle Pids aufgelistet.

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

Das erste Argument von list_offspring ist die übergeordnete pid

Merijn
quelle
1
Es gibt bereits mehrere andere Antworten, die ein Bash-Skript ergeben, das keinen tatsächlichen Baum ausgibt, einschließlich eines in der Frage selbst. Welche Vorteile hat Ihre (Teil-) Antwort gegenüber den vorhandenen (Teil-) Antworten?
David Richerby
Ich habe die Rekursion verwendet und eine Erklärung hinzugefügt. Dachte, es könnte jemandem helfen. Es macht genau das, was der Titel verlangt.
Merijn
Ich mag die Verwendung von pgrep hier, da ps unter Unix-Varianten unterschiedlich sein kann.
John Szakmeister
3

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

fügt keinen Baum per se hinzu, es ist nur die Liste der Prozesse.

gibt zum Beispiel

COMMAND
bash
  nvim
    python
edi9999
quelle
Mit dieser -HOption wird ein Prozessbaum oder eine "Prozesshierarchie (
Anthony G - Justice für Monica
2

Ich habe daran gearbeitet, eine Lösung für genau dasselbe Problem zu finden. Grundsätzlich dokumentiert ps manpage keine Option, die es erlaubt, mit einem einzigen Befehl das zu tun, was wir wollen. Fazit: Ein Skript wird benötigt.

Ich habe mir ein Skript ausgedacht, das Ihrem sehr ähnlich ist. Ich habe es in mein ~ / .bashrc eingefügt, damit ich es von jeder Shell aus verwenden kann.

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.
quelle
1

Unterwegs auf der Kommandozeile:

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

es gibt aus:

    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

eleganter Weg auf diese Weise ist es, eine Funktion zu definieren in .bashrc:

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

dann auf der Kommandozeile ausführen:

subps bash
Shakiba Moshiri
quelle
1

Dies gibt nur die pid + children pids in einer Zeile an, was für die weitere Verarbeitung nützlich ist.

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

Ich habe ein ähnliches Drehbuch basierend auf dem obigen von Philippe erstellt

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

Dies gibt alle Kinder-, Enkelkinder- usw. Pids in einem durch Leerzeichen getrennten Format aus. Dies kann wiederum ps zugeführt werden, wie in

ps -p $ (pidlist pid )

kylehutson
quelle
-2

für alle Prozesse - pstree - eine Show von Benutzer - pstree Benutzer

Andy Wong
quelle
2
Bitte erläutern Sie, warum dies die Frage beantwortet.
Benutzer 99572 ist in Ordnung