Jeder Linux-Administrator kennt ps. Je nach Parametern werden sehr detaillierte Prozessinformationen aufgelistet. U. a. welcher Prozess der übergeordnete ist usw. Die Ausgabe von ps ist dabei schon gerne einmal unübersichtlich und man muss schon suchen, um eine Struktur zu erkennen.
Um das zu vereinfachen, habe ich ein kleines Skript entworfen, welches sich in einer Schleife durch die Ausgabe von ps grept, bis der letzte Rattenschwanz einer Prozessstruktur erfasst worden ist. Das Prinzip ist dabei recht simpel: Man sucht nach einer Zeichenkette, das Srkipt geht jeden Eintrag durch, der dafür in Frage kommt, und sucht auch gleich die durch diesen Prozess aufgerufenen Subprozesse.
Hier nun das Skript:
#!/bin/bash
crawled=()
function contains() {
for e in "${crawled[@]}"
do
if [ "$e" == "$1" ]
then
echo "1"
return 0
fi
done
echo "0"
return 0
}
function crawlps() {
OIFS=$IFS
IFS=$'\n'
for ln in $(ps alx | sed 's/ \+/ /g' | cut -d' ' -f2,3,4,13- | sort -k3 -n | grep " $1 " | grep -v 'grep' | grep -v $3)
do
u=$(echo $ln | cut -d' ' -f1)
p1=$(echo $ln | cut -d' ' -f2)
p2=$(echo $ln | cut -d' ' -f3)
c=$(echo $ln | cut -d' ' -f4-)
if [ "$p1" == "$1" ] && [ "$(contains $1)" == "0" ]
then
for i in $(seq 1 $2)
do
echo -n ' '
done
echo "$p2 --> $p1: $u: $c"
crawled=("${crawled[@]}" "$1")
elif [ "$p2" == "$1" ]
then
crawlps $p1 $(($2+1)) $3
fi
done
IFS=$OIFS
}
OIFS=$IFS
IFS=$'\n'
for proc in $(ps alx | sed 's/ \+/ /g' | grep "$1" | grep -v ' PPID ' | grep -v 'grep' | grep -v ${0##*/} | cut -d' ' -f3,4 | sort -k2 -n)
do
crawlps $(echo $proc | cut -d' ' -f1) 0 ${0##*/}
done
IFS=$OIFS
Zur Erläuterung:
- Zunächst werden zwei Funktionen deklariert und definiert: contains und crawlps
- In der ersten wird das Array crawled nach einem übergebenen Wert durchsucht und entspr. 1 (Treffer) oder 0 (kein Treffer) ausgegeben.
- In der zweiten wird die Ausgabe von ps mit entspr. Parametern gefiltert, sortiert und formatiert. Das ergebnis wird in einer Schleife durchgegangen.
- Es wird geprüft, ob es sich in der Zeile um die übergebene Prozess-ID handelt. (In dem Fall werden die wesentlichen Informationen zu diesem Prozess aufgegeben:)
- Eltern-PID —> PID: User-ID: Vollständiger Aufruf
- Falls nicht, wird geprüft, ob es sich bei der übergebenen PID um die übergeordnete Prozess-ID handelt. (In dem Fall wird für diesen Prozess erneut die Funktion crawlps aufgerufen.)
- Nach der Funktion erfolgt der erste Aufruf und es wird nach einem übergebenen String gefiltert.
Ein Beispiel:
ansible@ansible:~$ ./test.sh ssh
1 --> 14134: 1000: ssh: /home/ansible/.ansible/cp/fa38416f61 [mux]
1 --> 14138: 1000: ssh: /home/ansible/.ansible/cp/0018b3053c [mux]
1 --> 14141: 1000: ssh: /home/ansible/.ansible/cp/2963d0ab38 [mux]
1 --> 14144: 1000: ssh: /home/ansible/.ansible/cp/69fa50d11b [mux]
1 --> 14187: 1000: ssh: /home/ansible/.ansible/cp/b8eef2364b [mux]
1 --> 14190: 1000: ssh: /home/ansible/.ansible/cp/d4cd3c5822 [mux]
1 --> 14193: 1000: ssh: /home/ansible/.ansible/cp/df3202863f [mux]
1 --> 14196: 1000: ssh: /home/ansible/.ansible/cp/52a7e46d0b [mux]
1 --> 14224: 1000: ssh: /home/ansible/.ansible/cp/d0234c0b50 [mux]
1 --> 14228: 1000: ssh: /home/ansible/.ansible/cp/56223fd03c [mux]
1 --> 14240: 1000: ssh: /home/ansible/.ansible/cp/6de55b42e5 [mux]
1 --> 14264: 1000: ssh: /home/ansible/.ansible/cp/b1f85a8b4d [mux]
1 --> 14281: 1000: ssh: /home/ansible/.ansible/cp/8d4319711b [mux]
1 --> 14284: 1000: ssh: /home/ansible/.ansible/cp/1a8d9b688f [mux]
1 --> 14301: 1000: ssh: /home/ansible/.ansible/cp/77145622b8 [mux]
1 --> 446: 0: /usr/sbin/sshd -D
446 --> 20782: 0: sshd: ansible [priv]
20782 --> 20784: 1000: sshd: ansible@pts/1
20784 --> 20785: 1000: -bash
Das Skript nimmt sich selbst aus der Ausgabe natürlich raus, sowie auch grep.