ps: Como posso recursivamente obter todo o processo filho para um determinado pid

37

Como posso obter toda a árvore de processos gerada por um determinado processo exibido como uma árvore e somente essa árvore, ou seja, sem outros processos?

A saída pode, e. parecido com

 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

Eu não consegui obter o resultado desejado puramente com parâmetros para ps .

O seguinte dá o resultado desejado, mas parece um pouco envolvido:

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

Alguém tem uma solução mais fácil?

    
por kynan 30.11.2011 / 22:25

10 respostas

30

O pstree é uma solução muito boa, mas é um pouco reticente. Eu uso ps --forest em vez disso. Mas não para PID ( -p ) porque imprime apenas o processo específico, mas para a sessão ( -g ). Ele pode imprimir qualquer informação ps pode imprimir em uma árvore de arte ASCII sofisticada definindo a opção -o .

Então, minha sugestão para esse problema:

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

Se o processo não for um líder de sessão, será necessário aplicar um pouco mais de truque:

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

Isso obtém primeiro o id da sessão (SID) do processo atual e depois chama ps novamente com aquele sid.

Se os cabeçalhos de coluna não forem necessários, adicione um '=' após cada definição de coluna nas opções '-o', como:

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

Um exemplo de execução e o 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

Infelizmente, isso não funciona para screen , pois define o sid para cada tela filha e todos os netos bash.

Para obter todos os processos gerados por um processo, toda a árvore precisa ser construída. Eu usei awk para isso. Inicialmente, ele cria uma matriz de hash para conter todos os PID => ,child,child... . No final, ele chama uma função recursiva para extrair todos os processos filhos de um determinado processo. O resultado é passado para outro ps para formatar o resultado. O PID real deve ser escrito como um argumento para awk em vez 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 um processo SCREEN (pid = 8041), o exemplo de saída se parece com isto:

  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
    
por 08.10.2014 / 11:11
24
pstree ${pid}

em que $ {pid} é o pid do processo pai.

No gentoo pstree está no pacote "psmisc", aparentemente localizado em link

    
por 30.11.2011 / 23:02
10

Aqui está minha versão que é executada instantaneamente (porque ps foi executada apenas uma vez). Trabalha em 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
)
    
por 16.07.2014 / 23:59
3

Eu criei um pequeno script bash para criar uma lista de pid's de processo (s) filho (s) pai (s). Recursivamente até encontrar o último processo filho que não possui filhos. Não lhe dá uma visão de árvore. Apenas lista todos os 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

primeiro argumento de list_offspring é o pai pid

    
por 17.06.2015 / 12:20
3

Eu tenho trabalhado para encontrar uma solução para exatamente o mesmo problema. Bascially, ps manpage não documenta nenhuma opção que permita fazer o que queremos com um único comando. Conclusão: um script é necessário.

Eu criei um script muito parecido com o seu. Eu colei no meu ~ / .bashrc para que eu possa usá-lo de qualquer 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
}
    
por 01.02.2012 / 22:05
3

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

não adiciona uma árvore por si só, é apenas a lista de processos.

dá por exemplo

COMMAND
bash
  nvim
    python
    
por 31.08.2016 / 17:29
1

A caminho da linha de comando:

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

gera:

    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

maneira mais elegante desse jeito é definir uma função em .bashrc :

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

e depois na linha de comando:

subps bash
    
por 06.09.2017 / 13:25
0

Eu fiz um roteiro similar baseado no de Philippe acima

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

Isso gera todos os filhos, netos, etc., em formato delimitado por espaços. Isto pode, por sua vez, ser alimentado ao ps, como em

ps -p $(pidlist pid)

    
por 25.06.2014 / 22:56
0

Isso fornece apenas o pid + children pids cada em uma linha, o que é útil para processamento posterior.

pidtree() {
    for _pid in "$@"; do
        echo $_pid
        pidtree 'ps --ppid $_pid -o pid h'
    done
}
    
por 25.11.2017 / 14:19
-2

para todo processo - pstree -a mostre pelo usuário - usuário pstree

    
por 17.03.2016 / 17:20