ps: ¿Cómo puedo obtener recursivamente todos los procesos secundarios para un pid dado?

43

¿Cómo puedo hacer que todo el árbol de procesos sea generado por un proceso dado que se muestra como un árbol y solo ese árbol, es decir, no hay otros procesos?

La salida podría, por ejemplo, verse como

 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

No pude obtener el resultado deseado simplemente con parámetros para ps.

Lo siguiente da el resultado deseado pero parece un poco complicado:

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

¿Alguien tiene una solución más fácil?

kynan
fuente
No es una respuesta, pero comienza con ps auxf.
jftuga
3
@jtfuga Esto es, de hecho, donde comencé, pero esto me da todos los procesos, que es exactamente lo que no quiero.
kynan

Respuestas:

38

El pstreees una muy buena solución, pero es un poco reticente bits. Yo uso ps --foresten su lugar. Pero no para a PID( -p) porque imprime solo el proceso específico, sino para la sesión ( -g). Puede imprimir cualquier información que pspueda imprimir en un elegante árbol de arte ASCII que define la -oopción.

Entonces mi sugerencia para este problema:

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

Si el proceso no es un líder de sesión, entonces se debe aplicar un poco más de truco:

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

Esto obtiene el ID de sesión (SID) del proceso actual primero y luego llama a ps nuevamente con ese sid.

Si los encabezados de columna no son necesarios, agregue un '=' después de cada definición de columna en las opciones '-o', como:

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

Un ejemplo de ejecución y el resultado:

$ 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

Desafortunadamente, esto no funciona, screenya que establece el sid para cada pantalla secundaria y para todos los nietos.

Para obtener todos los procesos generados por un proceso, es necesario construir todo el árbol. Solía para eso. Al principio construye una matriz hash para contener todo PID => ,child,child.... Al final, llama a una función recursiva para extraer todos los procesos secundarios de un proceso dado. El resultado se pasa a otro pspara formatear el resultado. El PID real debe escribirse como un argumento para lugar 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)}')

Para un proceso SCREEN (pid = 8041), el resultado de ejemplo se ve así:

  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
Verdad
fuente
El problema con esto (lo sé, es una respuesta anterior) es la opción --forest solo funciona en Linux (u otros comandos "ps" basados ​​en GNU). Solaris y MacOS no les gusta.
Armand
@TrueY, ¿sabes de una API de C que pueda hacer eso? Gracias
Bionix1441
Bionix No, lo siento ... puedo leer los directorios / proc / <PID> para construir ese árbol.
Verdaderamente
1
Parece que esto debería ser simple si pstuviera una opción de indicador de filtro solo para filtrar a todos los descendientes de un PID.
CMCDragonkai
27
pstree ${pid}

donde ${pid}está el pid del proceso padre.

En Gentoo Linux, pstreeestá en el paquete "psmisc", aparentemente ubicado enhttp://psmisc.sourceforge.net/

Eroen
fuente
9
Gracias, debería haber mencionado que había mirado pstree, pero me perdí un formato de salida más detallado. Sin embargo, pstree -p <pid>al menos imprima los pids que estén razonablemente cerca.
kynan
2
También tengo este problema, necesito reunir todos los pids infantiles de forma recursiva, pero solo necesito los pids, así que tendría que hacer sedtodo eso ... mmm esto funciona :)pstree -pn 4008 |grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*"
Aquarius Power
12

Aquí está mi versión que se ejecuta instantáneamente (porque se psejecuta solo una vez). Funciona en bash y 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
fuente
1
Esto también imprime el pid del proceso actual, que puede querer o no. No quería que se enumerara el proceso actual (solo descendientes del proceso actual), así que cambié el script moviéndome echo $1dentro del primer bucle for y cambiándolo a echo $i.
Mark Lakata
1
Estaba a punto de escribir tal función cuando encontré esto. Esta es la única solución que parece funcionar en MacOS, Linux y Solaris. Buen trabajo. Me encanta que confíes en una sola ejecución de ps para hacer el trabajo en la memoria.
Armand
3

He creado un pequeño script bash para crear una lista de pid de los procesos secundarios de un padre. Recurrentemente hasta que encuentre el último proceso hijo que no tiene ningún hijo. No te da una vista de árbol. Simplemente enumera todos los 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

primer argumento de list_offspring es el padre pid

Merijn
fuente
1
Ya hay varias otras respuestas que dan un script bash que no imprime un árbol real, incluido uno en la pregunta misma. ¿Qué ventajas tiene su respuesta (parcial) sobre las respuestas (parciales) existentes?
David Richerby
Usé la recursión y agregué alguna explicación. Pensé que podría ayudar a alguien. Hace exactamente lo que pide el título.
Merijn
Me gusta el uso de pgrep aquí, ya que ps puede diferir entre las variantes de Unix.
John Szakmeister el
3

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

no agrega un árbol per se, es solo la lista de procesos.

da por ejemplo

COMMAND
bash
  nvim
    python
edi9999
fuente
La -Hopción se utiliza para mostrar un árbol de procesos o "jerarquía de procesos (bosque)"
Anthony G - justicia para Mónica
2

He estado trabajando para encontrar una solución al mismo problema. Básicamente, ps manpage no documenta ninguna opción que permita hacer lo que queramos con un solo comando. Conclusión: se necesita un guión.

Se me ocurrió un guión muy similar al tuyo. Lo pegué en mi ~ / .bashrc para poder usarlo desde cualquier 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.
fuente
1

En camino en la línea de comandos:

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

La forma más elegante de esa manera es definir una función en .bashrc:

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

luego en la línea de comando ejecutar:

subps bash
Shakiba Moshiri
fuente
1

Esto proporciona solo los pid hijos pid + cada uno en una línea, lo cual es útil para su posterior procesamiento.

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

Hice un guión similar basado en lo anterior 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"
}

Esto genera todos los pids secundarios, nietos, etc. en formato delimitado por espacios. Esto, a su vez, puede ser alimentado a ps, como en

ps -p $ (pidlist pid )

kylehutson
fuente
-2

para todo el proceso - pstree -a show by user - pstree user

Andy Wong
fuente
2
Por favor explique un poco más por qué esto responde la pregunta.
usuario 99572 está bien