web-dev-qa-db-fra.com

ps: Comment puis-je obtenir récursivement tous les processus enfants pour un pid donné

Comment puis-je obtenir l’ensemble de l’arbre de processus généré par un processus donné affiché sous forme d’arbre et uniquement cet arbre c’est-à-dire aucun autre processus?

La sortie pourrait par exemple ressembler

 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

Je ne pouvais pas obtenir le résultat souhaité uniquement avec des paramètres de ps.

Ce qui suit donne le résultat souhaité mais semble un peu impliqué:

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

Quelqu'un at-il une solution plus facile?

40
kynan

La pstree est une très bonne solution, mais elle est un peu réticente. J'utilise ps --forest à la place. Mais pas pour une PID (-p) car elle imprime uniquement le processus spécifique, mais pour la session (-g). Il peut imprimer toutes les informations que ps peut imprimer dans une arborescence de fantaisie ASCII définissant l'option -o.

Donc, ma suggestion pour ce problème:

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

Si le processus n'est pas un chef de session, un truc un peu plus doit être appliqué:

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

Cela récupère d'abord l'identifiant de session (SID) du processus en cours, puis appelle à nouveau ps avec ce sid.

Si les en-têtes de colonne ne sont pas nécessaires, ajoutez un '=' après chaque définition de colonne dans les options '-o', comme suit:

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

Un exemple d'exécution et le résultat:

$ 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

Malheureusement, cela ne fonctionne pas pour screen car il définit le sid de chaque écran enfant et de tous les bash enfants.

Pour obtenir tous les processus engendrés par un processus, il faut construire l'arbre entier. J'ai utilisé awk pour cela. Au début, il construit un tableau de hachage contenant tout le PID => ,child,child.... À la fin, il appelle une fonction récursive pour extraire tous les processus enfants d'un processus donné. Le résultat est passé à une autre ps pour formater le résultat. Le PID réel doit être écrit en tant qu'argument pour awk au lieu de <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)}')

Pour un processus SCREEN (pid = 8041), l'exemple de sortie ressemble à ceci:

  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
32
TrueY
pstree ${pid}

où $ {pid} est le pid du processus parent.

Sur gentoo, pstree est dans le paquet "psmisc", apparemment situé à http://psmisc.sourceforge.net/

24
Eroen

Voici ma version qui s'exécute instantanément (car ps n'a été exécuté qu'une fois). Fonctionne en bash et 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
)
10
xzfc

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

n’ajoute pas d’arbre en soi, c’est juste la liste des processus.

donne par exemple

COMMAND
bash
  nvim
    python
3
edi9999

Je travaille pour trouver une solution au même problème. Fondamentalement, ps manpage ne documente aucune option permettant de faire ce que nous voulons avec une seule commande. Conclusion: un script est nécessaire.

Je suis venu avec un script très similaire au vôtre. Je l'ai collé dans mon ~/.bashrc pour que je puisse l'utiliser depuis n'importe quel 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
}
3
Philippe A.

J'ai créé un petit script bash pour créer une liste de pid des processus enfants d'un parent. Récursivement jusqu'à ce qu'il trouve le dernier processus enfant qui n'a aucun enfant. Cela ne vous donne pas une vue en arbre. Il ne liste que tous les 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

le premier argument de list_offspring est le pid parent

3
Merijn

Cela ne donne que les pid + enfants pids chacun sur une ligne, ce qui est utile pour un traitement ultérieur.

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

En route sur la ligne de commande:

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

il produit:

    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

manière plus élégante de cette manière est de définir une fonction dans .bashrc:

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

puis sur la ligne de commande exécutée:

subps bash
1
Shakiba Moshiri

J'ai fait un script similaire basé sur celui de 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"
}

Ceci affiche tous les pids enfants, petits-enfants, etc. au format délimité par des espaces. Cela peut, à son tour, être transmis à ps, comme dans

ps -p $ (pidlist pid )

0
kylehutson