ps: Jak mogę rekurencyjnie uzyskać cały proces potomny dla danego pid


43

Jak mogę wyświetlić całe drzewo procesów odrodzone przez dany proces jako drzewo i tylko to drzewo, tzn. Żadnych innych procesów?

Wynik może np. Wyglądać

 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

Nie mogłem uzyskać pożądanego rezultatu wyłącznie z parametrami do ps.

Poniższe wyniki dają pożądany rezultat, ale wydają się być trochę zaangażowane:

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

Czy ktoś ma łatwiejsze rozwiązanie?


Nie odpowiedź, ale zacznij od ps auxf.
jftuga

3
@ jtfuga To właściwie od tego zacząłem, ale daje mi to wszystkie procesy, czego dokładnie nie chcę.
kynan

Odpowiedzi:


38

To pstreebardzo dobre rozwiązanie, ale jest trochę powściągliwe. Używam ps --forestzamiast tego. Ale nie dla a PID( -p), ponieważ drukuje tylko określony proces, ale dla session ( -g). Może wydrukować dowolną informację, którą psmożna wydrukować w fantazyjnym drzewie graficznym ASCII określającym -oopcję.

Więc moja sugestia dotycząca tego problemu:

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

Jeśli proces nie jest liderem sesji, należy zastosować nieco więcej sztuczki:

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

Najpierw pobiera identyfikator sesji (SID) bieżącego procesu, a następnie ponownie wywołuje ps z tym identyfikatorem sid.

Jeśli nagłówki kolumn nie są potrzebne, dodaj „=” po każdej definicji kolumny w opcjach „-o”, takich jak:

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

Przykładowy przebieg i wynik:

$ 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

Niestety to nie działa, screenponieważ ustawia sid dla każdego ekranu potomnego i wszystkich bashów wnuka.

Aby wszystkie procesy zostały odrodzone przez proces, należy zbudować całe drzewo. Użyłem do tego . Najpierw buduje tablicę skrótów, która zawiera wszystko PID => ,child,child.... Na koniec wywołuje funkcję rekurencyjną, aby wyodrębnić wszystkie procesy potomne danego procesu. Wynik jest przekazywany do innego w pscelu sformatowania wyniku. Rzeczywisty PID musi być zapisany jako argument do zamiast <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)}')

W przypadku procesu SCREEN (pid = 8041) przykładowe dane wyjściowe wyglądają następująco:

  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

Problem z tym (wiem, to stara odpowiedź) polega na tym, że opcja --forest działa tylko na Linuksie (lub innych komendach ps opartych na GNU). Solaris i MacOS tego nie lubią.
Armand

@ Prawda Czy znasz interfejs API C, który to potrafi? Dziękuję
Bionix1441,

Bionix Nie, przepraszam ... Mogę przeczytać katalogi / proc / <PID>, aby zbudować to drzewo.
TrueY

1
Wygląda na to, że powinno to być proste, jeśli psmiałoby opcję flagi filtrowania, aby filtrować do wszystkich potomków PID.
CMCDragonkai

27
pstree ${pid}

gdzie ${pid}jest pid procesu nadrzędnego.

W Gentoo Linux pstreeznajduje się w pakiecie „psmisc”, najwyraźniej znajdującym się pod adresemhttp://psmisc.sourceforge.net/


9
Dzięki, powinienem wspomnieć, że obejrzałem pstree, ale przegapiłem bardziej szczegółowy format wyjściowy. Jednak pstree -p <pid>przynajmniej wydrukuj stawki, które są dość blisko.
kynan

2
Mam też ten problem, muszę rekursywnie zbierać wszystkie pids dla dzieci, ale potrzebuję tylko pids, więc musiałbym to sedwszystko ... mmm to działa :)pstree -pn 4008 |grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*"
Aquarius Power

12

Oto moja wersja, która działa natychmiast (ponieważ jest pswykonywana tylko raz). Działa w bash i 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
)

1
Spowoduje to również wydrukowanie pid bieżącego procesu, który możesz chcieć lub nie. Nie chciałem wyświetlać bieżącego procesu (tylko potomków bieżącego procesu), więc zmieniłem skrypt, przechodząc echo $1do pierwszej pętli for i zmieniając ją na echo $i.
Mark Lakata,

1
Właśnie miałem napisać taką funkcję, kiedy ją znalazłem. To jedyne rozwiązanie, które wydaje się działać w systemach MacOS, Linux i Solaris. Świetna robota. Uwielbiam to, że polegasz na jednym uruchomieniu ps, aby wykonać zadanie w pamięci.
Armand

3

Stworzyłem mały skrypt bash, aby utworzyć listę pid procesów potomnych rodzica. Rekurencyjnie, aż znajdzie ostatni proces potomny, który nie ma żadnych potomków. Nie daje widoku drzewa. Po prostu wyświetla wszystkie 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

pierwszym argumentem list_offspring jest pid nadrzędny


1
Istnieje już kilka innych odpowiedzi, które dają skrypt bash, który nie drukuje rzeczywistego drzewa, w tym jedna w samym pytaniu. Jakie zalety ma Twoja (częściowa) odpowiedź w stosunku do istniejących (częściowych) odpowiedzi?
David Richerby,

Użyłem rekurencji i dodałem wyjaśnienie. Myślałem, że to może komuś pomóc. Robi dokładnie to, o co prosi tytuł.
Merijn

Podoba mi się tutaj użycie pgrep, ponieważ ps może różnić się między wariantami unix.
John Szakmeister,


2

Pracowałem nad rozwiązaniem tego samego problemu. Zasadniczo ps manpage nie dokumentuje żadnej opcji pozwalającej robić to, co chcemy za pomocą jednego polecenia. Wniosek: potrzebny jest skrypt.

Wymyśliłem skrypt bardzo podobny do twojego. Wkleiłem go w moim ~ / .bashrc, dzięki czemu mogę go używać z dowolnej powłoki.

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
}

1

Po drodze w wierszu polecenia:

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

generuje:

    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

bardziej eleganckim sposobem jest zdefiniowanie funkcji w .bashrc:

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

następnie w wierszu poleceń:

subps bash

1

Daje to tylko pid + pid + pid dla każdego na jednej linii, co jest przydatne do dalszego przetwarzania.

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

0

Zrobiłem podobny skrypt na podstawie powyższego Philippe'a

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

Powoduje to wyświetlenie wszystkich pid podrzędnych, wnuków itp. W formacie rozdzielanym spacjami. To z kolei może być karmione ps, jak w

ps -p $ (pidlist pid )


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.