ps: Come posso ricorsivamente ottenere tutto il processo figlio per un determinato pid


43

Come posso ottenere l'intero albero di processo generato da un dato processo visualizzato come un albero e solo quell'albero, cioè nessun altro processo?

L'output potrebbe ad esempio apparire

 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

Non sono riuscito ad ottenere il risultato desiderato puramente con parametri ps.

Quanto segue dà il risultato desiderato ma sembra un po 'coinvolto:

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

Qualcuno ha una soluzione più semplice?


Non una risposta, ma inizia con ps auxf.
jftuga,

3
@jtfuga Questo è in effetti il ​​punto da cui ho iniziato, ma questo mi dà tutti i processi, che è esattamente ciò che non voglio.
kynan,

Risposte:


38

È pstreeun'ottima soluzione, ma è un po 'reticente. Io uso ps --forestinvece. Ma non per un PID( -p) perché stampa solo il processo specifico, ma per la sessione ( -g). Può stampare qualsiasi informazione pspossa essere stampata in un albero di fantasia in ASCII che definisce l' -oopzione.

Quindi il mio suggerimento per questo problema:

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

Se il processo non è un leader di sessione, è necessario applicare un altro trucco:

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

Questo ottiene prima l'ID sessione (SID) del processo corrente e quindi chiama di nuovo ps con quel sid.

Se le intestazioni di colonna non sono necessarie aggiungere un '=' dopo ogni definizione di colonna nelle opzioni '-o', come:

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

Una corsa di esempio e il risultato:

$ 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

Sfortunatamente questo non funziona screenperché imposta il sid per ogni schermata figlio e per tutti i nipoti bash.

Per ottenere tutti i processi generati da un processo, è necessario costruire l'intero albero. Ho usato per quello. Inizialmente crea un array di hash per contenere tutto PID => ,child,child.... Alla fine chiama una funzione ricorsiva per estrarre tutti i processi figlio di un dato processo. Il risultato viene passato a un altro psper formattare il risultato. Il PID effettivo deve essere scritto come argomento per anziché <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)}')

Per un processo SCREEN (pid = 8041) l'output di esempio è simile al seguente:

  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

Il problema con questo (lo so, è una vecchia risposta) è che l'opzione --forest funziona solo su Linux (o altri comandi "ps" basati su gnu). A Solaris e MacOS non piace.
Armand,

@VeroY conosci un'API C che può farlo? Grazie
Bionix1441

Bionix No, scusa ... Potrei leggere le directory / proc / <PID> per costruire quell'albero.
Vero

1
Sembra che questo dovrebbe essere semplice se psavesse un'opzione flag filtro solo per filtrare a tutti i discendenti di un PID.
CMCDragonkai,

27
pstree ${pid}

dove ${pid}è il pid del processo genitore.

Su Gentoo Linux, pstreeè apparentemente nel pacchetto "psmisc"http://psmisc.sourceforge.net/


9
Grazie, avrei dovuto dire che avevo guardato pstree, ma ho perso un formato di output più dettagliato. Tuttavia, pstree -p <pid>almeno stampare i pid che sono ragionevolmente vicini.
kynan,

2
Anch'io ho questo problema, ho bisogno di raccogliere tutti i pid bambino in modo ricorsivo ma ho bisogno solo dei pid, quindi dovrei fare sedtutto questo ... mmm, questo funziona :)pstree -pn 4008 |grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*"
Aquarius Power,

12

Ecco la mia versione che funziona istantaneamente (perché pseseguita solo una volta). Funziona in bash e 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
Questo stampa anche il pid del processo corrente, che potresti o meno desiderare. Non volevo elencare il processo corrente (solo i discendenti del processo corrente), quindi ho cambiato lo script spostandomi echo $1all'interno del primo per il ciclo e cambiandolo in echo $i.
Mark Lakata,

1
Stavo per scrivere una tale funzione quando l'ho trovata. Questa è l'unica soluzione che sembra funzionare su MacOS, Linux e Solaris. Ottimo lavoro. Adoro il fatto che ti affidi a una singola corsa di ps per portare a termine il lavoro in memoria.
Armand,

3

Ho creato un piccolo script bash per creare un elenco di pid dei processi figlio di un genitore. Ricorsivamente fino a quando non trova l'ultimo processo figlio che non ha figli. Non ti dà una vista ad albero. Elenca solo tutti i 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

il primo argomento di list_offspring è il pid padre


1
Esistono già molte altre risposte che danno uno script bash che non stampa un albero reale, incluso uno nella domanda stessa. Quali vantaggi ha la tua risposta (parziale) rispetto alle risposte (parziali) esistenti?
David Richerby,

Ho usato la ricorsione e ho aggiunto alcune spiegazioni. Ho pensato che potesse aiutare qualcuno. Fa esattamente quello che il titolo richiede.
Merijn,

Mi piace l'uso di pgrep qui, poiché ps può differire tra le varianti di unix.
John Szakmeister,

3

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

non aggiunge un albero in sé, è solo l'elenco dei processi.

dà ad esempio

COMMAND
bash
  nvim
    python

L' -Hopzione viene utilizzata per visualizzare un albero dei processi o "gerarchia dei processi (foresta)"
Anthony G - giustizia per Monica

2

Ho lavorato per trovare una soluzione allo stesso identico problema. Bascialmente, la manpage ps non documenta alcuna opzione che permetta di fare ciò che vogliamo con un singolo comando. Conclusione: è necessaria una sceneggiatura.

Mi è venuta in mente una sceneggiatura molto simile alla tua. L'ho incollato nel mio ~ / .bashrc in modo da poterlo usare da qualsiasi 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

Sulla strada dalla riga di comando:

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

produce:

    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

un modo più elegante di quel modo è definire una funzione in .bashrc:

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

quindi sulla riga di comando eseguire:

subps bash

1

Ciò fornisce solo i pid pid + children ciascuno su una riga, utile per ulteriori elaborazioni.

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

0

Ho realizzato una sceneggiatura simile basata su quella di Philippe sopra

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

Ciò genera tutti i pid figlio, nipote, ecc. In formato delimitato da spazi. Questo, a sua volta, può essere alimentato a ps, come in

ps -p $ (pidlist pid )


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.