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

43

Comment puis-je obtenir la totalité de l'arborescence de processus générée par un processus donné affichée sous forme d'arbre et uniquement cet arbre, c'est-à-dire sans 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 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?

Kynan
la source
Pas une réponse, mais commencez par ps auxf.
Jftuga
3
@jtfuga C'est en fait où j'ai commencé, mais cela me donne tous les processus, ce qui est exactement ce que je ne veux pas .
Kynan

Réponses:

38

C'est pstreeune très bonne solution, mais c'est un peu réticent. Je l'utilise ps --forestplutôt. Mais pas pour a PID( -p) car il affiche uniquement le processus spécifique, mais pour la session ( -g). Il peut imprimer n'importe quelle information pspeut être imprimé dans une arborescence de fantaisie ASCII définissant l' -ooption.

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 screencar il définit le sid pour chaque écran enfant et tous les bash enfants.

Pour obtenir tous les processus engendrés par un processus, il faut construire l'arbre entier. J'ai utilisé pour ça. Au début, il construit un tableau de hachage pour tout contenir 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é à un autre pspour formater le résultat. Le PID réel doit être écrit comme argument pour 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
Vrai
la source
Le problème avec ceci (je sais que c'est une vieille réponse) est que l'option --forest ne fonctionne que sous Linux (ou d'autres commandes "ps" basées sur gnu). Solaris et MacOS ne l'aiment pas.
Armand
@TrueY connaissez-vous une API C capable de le faire? Merci
Bionix1441
Bionix Non, désolé ... Je peux lire les répertoires / proc / <PID> pour construire cet arbre.
TrueY
1
On dirait que cela devrait être simple si vous psaviez une option d'indicateur de filtre pour filtrer tous les descendants d'un PID.
CMCDragonkai
27
pstree ${pid}

${pid}est le pid du processus parent.

Sous Gentoo Linux, pstreele paquet "psmisc" se trouve apparemment àhttp://psmisc.sourceforge.net/

Eroen
la source
9
Merci, aurait dû mentionner que j'avais regardé pstree, mais raté un format de sortie plus prolixe. Cependant, pstree -p <pid>imprimez au moins les pids qui sont raisonnablement proches.
kynan
2
J'ai ce problème aussi, j'ai besoin de rassembler tous les enfants pids mais je n'ai besoin que des pids, alors il me faudrait sedtout ça .. mmm ça marche :)pstree -pn 4008 |grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*"
Aquarius Power
12

Voici ma version qui tourne instantanément (car psexécutée une seule 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
)
xzfc
la source
1
Cela affiche également le pid du processus en cours, que vous ne voulez peut-être pas ou pas. Je ne voulais pas que le processus en cours soit répertorié (juste les descendants du processus en cours), j'ai donc changé le script en déplaçant echo $1la première boucle for en le changeant en echo $i.
Mark Lakata
1
J'étais sur le point d'écrire une telle fonction quand j'ai trouvé ceci. C'est la seule solution qui semble fonctionner sur MacOS, Linux et Solaris. Bon travail. J'adore que vous comptiez sur une seule série de ps pour faire le travail en mémoire.
Armand
3

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

Merijn
la source
1
Il existe déjà plusieurs autres réponses donnant un script bash qui n'imprime pas un arbre, y compris une dans la question elle-même. Quels sont les avantages de votre réponse (partielle) par rapport aux réponses existantes (partielles)?
David Richerby
J'ai utilisé la récursivité et ajouté quelques explications. Je pensais que cela pourrait aider quelqu'un. Il fait exactement ce que le titre demande.
Merijn
J'aime utiliser pgrep ici, car ps peut différer selon les variantes unix.
John Szakmeister
3

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
edi9999
la source
L' -Hoption est utilisée pour afficher un arbre de processus ou "hiérarchie de processus (forêt)"
Anthony G - justice pour Monica
2

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

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
Shakiba Moshiri
la source
1

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
}
Ole Tange
la source
0

J'ai fait un scénario 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"
}

Tous les pid enfants, petits-enfants, etc. sont générés dans un format délimité par des espaces. Cela peut, à son tour, être transmis à ps, comme dans

ps -p $ (pidlist pid )

Kylehutson
la source
-2

pour tout processus - pstree -a show by user - utilisateur pstree

Andy Wong
la source
2
S'il vous plaît expliquer un peu plus pourquoi cela répond à la question.
utilisateur 99572 va bien