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

43

Como posso obter toda a árvore de processos gerada por um determinado processo exibida como uma árvore e somente essa árvore, ou seja, nenhum outro processo?

A saída poderia, por exemplo, parecer

 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

Não consegui obter o resultado desejado apenas com parâmetros para ps.

A seguir, 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?

kynan
fonte
Não é uma resposta, mas comece com ps auxf.
jftuga
3
@ jtfuga Foi aqui que comecei, mas isso me dá todos os processos, exatamente o que não quero.
Kynan

Respostas:

38

A pstreesolução é muito boa, mas é um pouco reticente. Eu uso em ps --forestvez disso. Mas não para um PID( -p) porque imprime apenas o processo específico, mas para a sessão ( -g). Ele pode imprimir qualquer informação que pspossa ser impressa em uma árvore de arte ASCII sofisticada, definindo a -oopção.

Então, minha sugestão para este 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 o ID da sessão (SID) do processo atual primeiro e depois chama ps novamente com esse sid.

Se os cabeçalhos das colunas 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 screen, pois define o sid para cada tela filho e para todos os netos.

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

  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
TrueY
fonte
O problema com isso (eu sei, é uma resposta antiga) é a opção --forest só funciona no Linux (ou em outros comandos "ps" baseados em gnu). Solaris e MacOS não gostam disso.
Armand
@ Verdadeiramente, você conhece uma API C que pode fazer isso? Obrigado
Bionix1441
Bionix Não, desculpe ... Eu posso ler os diretórios / proc / <PID> para construir essa árvore.
TrueY
1
Parece que isso deveria ser simples se pstivesse uma opção de sinalizador de filtro apenas para filtrar todos os descendentes de um PID.
precisa saber é o seguinte
27
pstree ${pid}

Onde ${pid}está o pid do processo pai.

No Gentoo Linux, pstreeestá no pacote "psmisc", aparentemente localizado emhttp://psmisc.sourceforge.net/

Eroen
fonte
9
Obrigado, deveria ter mencionado que eu tinha olhado pstree, mas perdi um formato de saída mais detalhado. No entanto, pstree -p <pid>pelo menos imprima os pids razoavelmente próximos.
Kynan
2
Eu tenho esse problema também, eu preciso reunir todos os pids criança de forma recursiva, mas eu preciso apenas os PIDs, então eu teria que sedtudo o que .. mmm funciona este :)pstree -pn 4008 |grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*"
Aquarius Poder
12

Aqui está minha versão que roda instantaneamente (porque psexecutada apenas uma vez). Funciona 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
)
xzfc
fonte
1
Isso também imprime o pid do processo atual, que você pode ou não querer. Como eu não queria o processo atual listado (apenas descendentes do processo atual), mudei o script movendo-me echo $1dentro do primeiro loop for e alterando-o para echo $i.
Mark Lakata
1
Eu estava prestes a escrever uma função quando encontrei isso. Esta é a única solução que parece funcionar no MacOS, Linux e Solaris. Ótimo trabalho. Amor que você confia em uma única série de ps para fazer o trabalho na memória.
Armand
3

Eu criei um pequeno script bash para criar um pid de lista dos processos filhos de um pai. Recursivamente até encontrar o último processo filho que não tem filhos. Não oferece uma visualização em árvore. Apenas lista todos os pid's.

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

Merijn
fonte
1
Já existem várias outras respostas que fornecem um script bash que não imprime uma árvore real, incluindo uma na própria pergunta. Quais são as vantagens da sua resposta (parcial) sobre as respostas existentes (parciais)?
David Richerby
Usei recursão e adicionei algumas explicações. Pensei que poderia ajudar alguém. Ele faz exatamente o que o título pede.
111115 Merijn
Eu gosto do uso do pgrep aqui, pois o ps pode diferir entre as variantes do unix.
John Szakmeister 2/16/16
3

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

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

dá por exemplo

COMMAND
bash
  nvim
    python
edi9999
fonte
A -Hopção é usada para exibir uma árvore de processos ou "hierarquia de processos (floresta)"
Anthony G - justice para Monica
2

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

Eu vim com um script muito parecido com o seu. Eu colei no meu ~ / .bashrc para que eu possa usá-lo em 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
}
Philippe A.
fonte
1

A caminho na linha de comando:

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

produz:

    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

A maneira mais elegante dessa maneira é definir uma função em .bashrc:

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

em seguida, na linha de comando, execute:

subps bash
Shakiba Moshiri
fonte
1

Isso fornece apenas os pid + filhos, cada um em uma linha, o que é útil para processamento adicional.

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

Eu criei um script semelhante 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 pids filhos, netos etc. no formato delimitado por espaço. Por sua vez, isso pode ser alimentado ao ps, como em

ps -p $ (pidlist pid )

kylehutson
fonte
-2

para todo o processo - pstree -a show by user - pstree user

Andy Wong
fonte
2
Por favor, explique um pouco mais por que isso responde à pergunta.
usuário 99572 está bem