Ich möchte einen ganzen Prozessbaum töten. Was ist der beste Weg, dies mit gängigen Skriptsprachen zu tun? Ich suche eine einfache Lösung.
chronos
oder herodes
.
Ich möchte einen ganzen Prozessbaum töten. Was ist der beste Weg, dies mit gängigen Skriptsprachen zu tun? Ich suche eine einfache Lösung.
chronos
oder herodes
.
Antworten:
Sie sagen nicht, ob der Baum, den Sie töten möchten, eine einzelne Prozessgruppe ist. (Dies ist häufig der Fall, wenn der Baum das Ergebnis eines Forkings von einem Serverstart oder einer Shell-Befehlszeile ist.) Sie können Prozessgruppen mithilfe von GNU ps wie folgt ermitteln:
ps x -o "%p %r %y %x %c "
Wenn es sich um eine Prozessgruppe handelt, die Sie beenden möchten, verwenden Sie einfach den kill(1)
Befehl. Geben Sie jedoch anstelle einer Prozessnummer die Negation der Gruppennummer an. Verwenden Sie beispielsweise, um jeden Prozess in Gruppe 5112 abzubrechen kill -TERM -- -5112
.
pgrep
kann eine einfachere Möglichkeit bieten, die Prozessgruppen-ID zu finden. Führen Sie beispielsweise aus, um die Prozessgruppe von my-script.sh zu beenden kill -TERM -$(pgrep -o my-script.sh)
.
ps -o pid --no-headers --ppid $PARENT_PID
ps x -o "%r %p %y %x %c" | sort -nk1,2
Beenden Sie alle Prozesse, die zum selben Prozessbaum gehören, mit der Prozessgruppen-ID ( PGID
)
kill -- -$PGID
Standard-Signal verwenden ( TERM
= 15)kill -9 -$PGID
Verwende das Signal KILL
(9)Sie können die PGID
von jeder Prozess-ID ( PID
) desselben Prozessbaums abrufen
kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')
(Signal TERM
)kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')
(Signal KILL
)Besonderer Dank geht an Tanager und Speakus für Beiträge zu $PID
verbleibenden Speicherplätzen und OSX-Kompatibilität.
kill -9 -"$PGID"
=> Signal 9 ( KILL
) an alle Kinder und Enkel senden ...PGID=$(ps opgid= "$PID")
=> Ruft die Prozessgruppen-ID von einer beliebigen Prozess-ID des Baums ab, nicht nur von der Prozess-Eltern-ID . Eine Variation von ps opgid= $PID
ist, ps -o pgid --no-headers $PID
wo pgid
durch ersetzt werden kann pgrp
. ps
Fügt führende Leerzeichen ein, wenn sie PID
weniger als fünf Stellen haben und rechts ausgerichtet sind, wie vom Tanager bemerkt . Sie können verwenden:PGID=$(ps opgid= "$PID" | tr -d ' ')
ps
Von OSX aus drucken Sie immer den Header, daher schlägt Speakus vor:PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
grep -o [0-9]*
druckt nur aufeinanderfolgende Ziffern (druckt keine Leerzeichen oder alphabetischen Überschriften).PGID=$(ps -o pgid= $PID | grep -o [0-9]*)
kill -TERM -"$PGID" # kill -15
kill -INT -"$PGID" # correspond to [CRTL+C] from keyboard
kill -QUIT -"$PGID" # correspond to [CRTL+\] from keyboard
kill -CONT -"$PGID" # restart a stopped process (above signals do not kill it)
sleep 2 # wait terminate process (more time if required)
kill -KILL -"$PGID" # kill -9 if it does not intercept signals (or buggy)
kill
von einem Prozess aufgerufen wird, der zum selben Baum gehört,kill
Gefahr, dass sie sich selbst töten bevor die gesamte beendet wird.> cat run-many-processes.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./child.sh background &
./child.sh foreground
echo "ProcessID=$$ ends ($0)"
> cat child.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./grandchild.sh background &
./grandchild.sh foreground
echo "ProcessID=$$ ends ($0)"
> cat grandchild.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
sleep 9999
echo "ProcessID=$$ ends ($0)"
Führen Sie den Prozessbaum im Hintergrund mit '&' aus.
> ./run-many-processes.sh &
ProcessID=28957 begins (./run-many-processes.sh)
ProcessID=28959 begins (./child.sh)
ProcessID=28958 begins (./child.sh)
ProcessID=28960 begins (./grandchild.sh)
ProcessID=28961 begins (./grandchild.sh)
ProcessID=28962 begins (./grandchild.sh)
ProcessID=28963 begins (./grandchild.sh)
> PID=$! # get the Parent Process ID
> PGID=$(ps opgid= "$PID") # get the Process Group ID
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 28969 Ss 33021 0:00 -bash
28349 28957 28957 28349 pts/3 28969 S 33021 0:00 \_ /bin/sh ./run-many-processes.sh
28957 28958 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./child.sh background
28958 28961 28957 28349 pts/3 28969 S 33021 0:00 | | \_ /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3 28969 S 33021 0:00 | | | \_ sleep 9999
28958 28963 28957 28349 pts/3 28969 S 33021 0:00 | | \_ /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3 28969 S 33021 0:00 | | \_ sleep 9999
28957 28959 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./child.sh foreground
28959 28960 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3 28969 S 33021 0:00 | | \_ sleep 9999
28959 28962 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3 28969 S 33021 0:00 | \_ sleep 9999
28349 28969 28969 28349 pts/3 28969 R+ 33021 0:00 \_ ps fj
Der Befehl pkill -P $PID
tötet das Enkelkind nicht:
> pkill -P "$PID"
./run-many-processes.sh: line 4: 28958 Terminated ./child.sh background
./run-many-processes.sh: line 4: 28959 Terminated ./child.sh foreground
ProcessID=28957 ends (./run-many-processes.sh)
[1]+ Done ./run-many-processes.sh
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 28987 Ss 33021 0:00 -bash
28349 28987 28987 28349 pts/3 28987 R+ 33021 0:00 \_ ps fj
1 28963 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28962 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28961 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28960 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
Der Befehl beendet kill -- -$PGID
alle Prozesse einschließlich des Enkels.
> kill -- -"$PGID" # default signal is TERM (kill -15)
> kill -CONT -"$PGID" # awake stopped processes
> kill -KILL -"$PGID" # kill -9 to be sure
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 29039 Ss 33021 0:00 -bash
28349 29039 29039 28349 pts/3 29039 R+ 33021 0:00 \_ ps fj
Ich bemerke in diesem Beispiel PID
und bin PGID
gleich ( 28957
).
Deshalb dachte ich ursprünglich, es kill -- -$PID
sei genug. Für den Fall, dass der Prozess innerhalb einer erzeugt wird, unterscheidet sich Makefile
die Prozess-ID von der Gruppen-ID .
Ich denke, es kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)
ist der beste einfache Trick, einen ganzen Prozessbaum zu töten, wenn er von einer anderen Gruppen-ID (einem anderen Prozessbaum) aufgerufen wird .
kill
sollte das Signal immer an den gesamten Baum senden, bevor ich ein eigenes Signal empfange. Unter bestimmten Umständen / Implementierungen kill
kann das Signal jedoch an sich selbst gesendet, unterbrochen und dann ein eigenes Signal empfangen werden. Das Risiko sollte jedoch minimal genug sein und kann in den meisten Fällen ignoriert werden, da andere Fehler vor diesem auftreten sollten. Kann dieses Risiko in Ihrem Fall ignoriert werden? Darüber hinaus haben andere Antworten diesen häufigen Fehler (ein kill
Teil des Prozessbaums wird getötet). Hoffe diese Hilfe .. Prost;)
man
tun das. Auf der anderen Seite funktioniert kill -- -$pid
es nicht , wenn Sie den gandchild-Prozess aus dem untergeordneten Prozess beenden möchten . Es ist also keine generische Lösung.
child.sh
.
pkill -TERM -P 27888
Dadurch werden alle Prozesse beendet, die die übergeordnete Prozess-ID 27888 haben.
Oder robuster:
CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS
welcher Zeitplan 33 Sekunden später tötet und die Prozesse höflich auffordert, zu beenden.
In dieser Antwort finden Sie Informationen zum Beenden aller Nachkommen.
pkill -P
sendet das Signal nur an das Kind => das Enkelkind empfängt das Signal nicht => Deshalb habe ich eine andere Antwort geschrieben , um das zu erklären. Prost ;-)
pkill -TERM -P ${$}
.
$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
Verwenden Sie killtree (), um einen Prozessbaum rekursiv zu beenden:
#!/bin/bash
killtree() {
local _pid=$1
local _sig=${2:--TERM}
kill -stop ${_pid} # needed to stop quickly forking parent from producing children between child killing and parent killing
for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
killtree ${_child} ${_sig}
done
kill -${_sig} ${_pid}
}
if [ $# -eq 0 -o $# -gt 2 ]; then
echo "Usage: $(basename $0) <pid> [signal]"
exit 1
fi
killtree $@
--
Argumente, ps
um unter OS X nicht zu funktionieren. Damit es dort funktioniert, ersetzen Sie den ps
Befehl durch: ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g
wo _regex
vor der for
Schleife definiert ist :local _regex="[ ]*([0-9]+)[ ]+${_pid}"
killtree()
zuverlässig funktioniert?
ps
nicht unterstützt --ppid
, kann man pgrep -P ${_pid}
stattdessen verwenden
Brads Antwort würde ich auch empfehlen, außer dass Sie sie awk
ganz abschaffen können, wenn Sie die --ppid
Option dazu verwenden ps
.
for child in $(ps -o pid -ax --ppid $PPID) do ....... done
Wenn Sie wissen, dass Sie die PID des übergeordneten Prozesses übergeben haben, finden Sie hier ein Shell-Skript, das funktionieren sollte:
for child in $(ps -o pid,ppid -ax | \
awk "{ if ( \$2 == $pid ) { print \$1 }}")
do
echo "Killing child process $child because ppid = $pid"
kill $child
done
Ich verwende eine etwas modifizierte Version einer hier beschriebenen Methode: https://stackoverflow.com/a/5311362/563175
So sieht es aus:
kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`
Dabei ist 24901 die PID des Elternteils.
Es sieht ziemlich hässlich aus, macht aber seinen Job perfekt.
pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
-l
zu pstree
, so lange Linien erhalten abgeschnitten nicht; es kann einfacher gemacht werden, auch mit zu lesen kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`
(keine Notwendigkeit, \n
in den Raum zu konvertieren , da es gut funktioniert), danke!
Geänderte Version von Zhigangs Antwort:
#!/usr/bin/env bash
set -eu
killtree() {
local pid
for pid; do
kill -stop $pid
local cpid
for cpid in $(pgrep -P $pid); do
killtree $cpid
done
kill $pid
kill -cont $pid
wait $pid 2>/dev/null || true
done
}
cpids() {
local pid=$1 options=${2:-} space=${3:-}
local cpid
for cpid in $(pgrep -P $pid); do
echo "$space$cpid"
if [[ "${options/a/}" != "$options" ]]; then
cpids $cpid "$options" "$space "
fi
done
}
while true; do sleep 1; done &
cpid=$!
for i in $(seq 1 2); do
cpids $$ a
sleep 1
done
killtree $cpid
echo ---
cpids $$ a
wait $pid
nur auf Prozesse, die Sie gestartet haben, nicht alle Prozesse, so dass dies keine generische Lösung ist
wait
wird unterdrücken Terminated
Nachricht , wenn es ein Kind ist.
Ich kann nicht kommentieren (nicht genug Ruf), daher bin ich gezwungen, eine neue Antwort hinzuzufügen , obwohl dies nicht wirklich eine Antwort ist.
Es gibt ein kleines Problem mit der ansonsten sehr schönen und gründlichen Antwort von @olibre am 28. Februar. Die Ausgabe von ps opgid= $PID
enthält führende Leerzeichen für eine PID mit weniger als fünf Ziffern, da dies ps
die Spalte rechtfertigt (die Zahlen genau ausrichten). Innerhalb der gesamten Befehlszeile führt dies zu einem negativen Vorzeichen, gefolgt von Leerzeichen, gefolgt von der Gruppen-PID. Einfache Lösung ist mit dem Rohr ps
zu tr
zu entfernen Plätzen:
kill -- -$( ps opgid= $PID | tr -d ' ' )
Um die Antwort von Norman Ramsey zu ergänzen, lohnt es sich möglicherweise, sich setsid anzusehen, wenn Sie eine Prozessgruppe erstellen möchten.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html
Die Funktion setsid () erstellt eine neue Sitzung, wenn der aufrufende Prozess kein Prozessgruppenleiter ist. Bei der Rückkehr ist der aufrufende Prozess der Sitzungsleiter dieser neuen Sitzung, der Prozessgruppenleiter einer neuen Prozessgruppe und hat kein kontrollierendes Terminal. Die Prozessgruppen-ID des aufrufenden Prozesses wird gleich der Prozess-ID des aufrufenden Prozesses gesetzt. Der aufrufende Prozess ist der einzige Prozess in der neuen Prozessgruppe und der einzige Prozess in der neuen Sitzung.
Was ich damit meine, dass Sie vom Startvorgang an eine Gruppe erstellen können. Ich habe dies in PHP verwendet, um einen ganzen Prozessbaum nach dem Start töten zu können.
Dies kann eine schlechte Idee sein. Ich würde mich für Kommentare interessieren.
Inspiriert von Ysths Kommentar
kill -- -PGID
Anstatt ihm eine Prozessnummer zu geben, geben Sie ihm die Negation der Gruppennummer. Wie bei fast jedem Befehl üblich, sollten Sie ein normales Argument, das mit a beginnt
-
und nicht als Schalter interpretiert werden soll, voranstellen--
PGID
aus PID
. Was denken Sie? Cheers
Die folgende Shell-Funktion ähnelt vielen anderen Antworten, funktioniert jedoch sowohl unter Linux als auch unter BSD (OS X usw.) ohne externe Abhängigkeiten wie pgrep
:
killtree() {
local parent=$1 child
for child in $(ps -o ppid= -o pid= | awk "\$1==$parent {print \$2}"); do
killtree $child
done
kill $parent
}
$child
dieser Funktion, um andere (nicht lokale) Variablen mit demselben Namen nicht zu stören und um sicherzustellen, dass der Wert der lokalen Variablen nach Beendigung der Funktion bereinigt wird.
Mit Python mit Psutil ist das ganz einfach . Installieren Sie einfach psutil mit pip und Sie haben eine vollständige Suite von Prozessmanipulations-Tools:
def killChildren(pid):
parent = psutil.Process(pid)
for child in parent.get_children(True):
if child.is_running():
child.terminate()
Wenn Sie einen Prozess mit Namen beenden möchten:
killall -9 -g someprocessname
oder
pgrep someprocessname | xargs pkill -9 -g
Dies ist meine Version des Beendens aller untergeordneten Prozesse mit dem Bash-Skript. Es verwendet keine Rekursion und hängt vom Befehl pgrep ab.
Verwenden
killtree.sh PID SIGNAL
Inhalt von killtrees.sh
#!/bin/bash
PID=$1
if [ -z $PID ];
then
echo "No pid specified"
fi
PPLIST=$PID
CHILD_LIST=`pgrep -P $PPLIST -d,`
while [ ! -z "$CHILD_LIST" ]
do
PPLIST="$PPLIST,$CHILD_LIST"
CHILD_LIST=`pgrep -P $CHILD_LIST -d,`
done
SIGNAL=$2
if [ -z $SIGNAL ]
then
SIGNAL="TERM"
fi
#do substring from comma to space
kill -$SIGNAL ${PPLIST//,/ }
Hier ist eine Variation von @ zhigangs Antwort, die ohne AWK auskommt und sich nur auf Bashs native Parsing-Möglichkeiten stützt:
function killtree {
kill -STOP "$1"
ps -e -o pid= -o ppid= | while read -r pid ppid
do
[[ $ppid = $1 ]] || continue
killtree "$pid" || true # Skip over failures
done
kill -CONT "$1"
kill -TERM "$1"
}
Es scheint sowohl auf Macs als auch auf Linux gut zu funktionieren. In Situationen, in denen Sie sich nicht darauf verlassen können, Prozessgruppen verwalten zu können - beispielsweise beim Schreiben von Skripten zum Testen einer Software, die in mehreren Umgebungen erstellt werden muss - ist diese Tree-Walking-Technik auf jeden Fall hilfreich.
Danke für deine Weisheit, Leute. Mein Skript hinterließ beim Beenden einige untergeordnete Prozesse, und der Negationstipp erleichterte die Arbeit. Ich habe diese Funktion geschrieben, um sie bei Bedarf in anderen Skripten zu verwenden:
# kill my group's subprocesses: killGroup
# kill also myself: killGroup -x
# kill another group's subprocesses: killGroup N
# kill that group all: killGroup -x N
# N: PID of the main process (= process group ID).
function killGroup () {
local prid mainpid
case $1 in
-x) [ -n "$2" ] && kill -9 -$2 || kill -9 -$$ ;;
"") mainpid=$$ ;;
*) mainpid=$1 ;;
esac
prid=$(ps ax -o pid,pgid | grep $mainpid)
prid=${prid//$mainpid/}
kill -9 $prid 2>/dev/null
return
}
Prost.
Es ist wahrscheinlich besser, die Eltern vor den Kindern zu töten; Andernfalls kann der Elternteil wahrscheinlich wieder neue Kinder hervorbringen, bevor er selbst getötet wird. Diese werden das Töten überleben.
Meine Version von ps unterscheidet sich von der oben genannten; vielleicht zu alt, deshalb das seltsame grepping ...
Die Verwendung eines Shell-Skripts anstelle einer Shell-Funktion hat viele Vorteile ...
Es ist jedoch im Grunde Zhigangs Idee
#!/bin/bash
if test $# -lt 1 ; then
echo >&2 "usage: kiltree pid (sig)"
fi ;
_pid=$1
_sig=${2:-TERM}
_children=$(ps j | grep "^[ ]*${_pid} " | cut -c 7-11) ;
echo >&2 kill -${_sig} ${_pid}
kill -${_sig} ${_pid}
for _child in ${_children}; do
killtree ${_child} ${_sig}
done
Das Folgende wurde unter FreeBSD, Linux und MacOS X getestet und hängt nur von pgrep und kill ab (die ps-o-Versionen funktionieren nicht unter BSD). Das erste Argument ist die Eltern-PID, deren Kinder beendet werden müssen. Das zweite Argument ist ein Boolescher Wert, um zu bestimmen, ob die übergeordnete PID ebenfalls beendet werden muss.
KillChilds() {
local pid="${1}"
local self="${2:-false}"
if children="$(pgrep -P "$pid")"; then
for child in $children; do
KillChilds "$child" true
done
fi
if [ "$self" == true ]; then
kill -s SIGTERM "$pid" || (sleep 10 && kill -9 "$pid" &)
fi
}
KillChilds $$ > /dev/null 2>&1
Dadurch wird SIGTERM an einen untergeordneten Prozess innerhalb eines Shell-Skripts gesendet. Wenn SIGTERM nicht erfolgreich ist, wartet es 10 Sekunden und sendet dann kill.
Frühere Antwort:
Das Folgende funktioniert auch, wird aber die Shell selbst auf BSD töten.
KillSubTree() {
local parent="${1}"
for child in $(ps -o pid=$parent); do
if [ $$ -ne $child ]; then (kill -s SIGTERM $child || (sleep 10 && kill -9 $child & )) > /dev/null 2>&1 ; fi
done
}
# Example lanch from within script
KillSubTree $$ > /dev/null 2>&1
Ich entwickle die Lösung von Zhigang, Xyuri und Solidsneck weiter:
#!/bin/bash
if test $# -lt 1 ; then
echo >&2 "usage: kiltree pid (sig)"
exit 1 ;
fi ;
_pid=$1
_sig=${2:-TERM}
# echo >&2 "killtree($_pid) mypid = $$"
# ps axwwf | grep -6 "^[ ]*$_pid " >&2 ;
function _killtree () {
local _children
local _child
local _success
if test $1 -eq $2 ; then # this is killtree - don't commit suicide!
echo >&2 "killtree can´t kill it´s own branch - some processes will survive." ;
return 1 ;
fi ;
# this avoids that children are spawned or disappear.
kill -SIGSTOP $2 ;
_children=$(ps -o pid --no-headers --ppid $2) ;
_success=0
for _child in ${_children}; do
_killtree $1 ${_child} $3 ;
_success=$(($_success+$?)) ;
done ;
if test $_success -eq 0 ; then
kill -$3 $2
fi ;
# when a stopped process is killed, it will linger in the system until it is continued
kill -SIGCONT $2
test $_success -eq 0 ;
return $?
}
_killtree $$ $_pid $_sig
Diese Version vermeidet das Töten ihrer Vorfahren - was in den vorherigen Lösungen zu einer Flut von untergeordneten Prozessen führt.
Prozesse werden ordnungsgemäß gestoppt, bevor die untergeordnete Liste festgelegt wird, sodass keine neuen untergeordneten Elemente erstellt werden oder verschwinden.
Nach dem Tod müssen die gestoppten Jobs weiterhin aus dem System verschwinden.
Alte Frage, ich weiß, aber alle Antworten scheinen weiterhin ps zu nennen, was mir nicht gefallen hat.
Diese awk-basierte Lösung erfordert keine Rekursion und ruft ps nur einmal auf.
awk 'BEGIN {
p=1390
while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2
o=1
while (o==1) {
o=0
split(p, q, " ")
for (i in q) if (a[q[i]]!="") {
p=p""a[q[i]]
o=1
a[q[i]]=""
}
}
system("kill -TERM "p)
}'
Oder in einer Zeile:
awk 'BEGIN {p=1390;while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2;o=1;while (o==1) {o=0;split(p, q, " ");for (i in q) {if (a[q[i]]!="") {p=p""a[q[i]];o=1;a[q[i]]=""}}}system("kill -TERM "p)}'
Grundsätzlich besteht die Idee darin, ein Array (a) von Parent: Child-Einträgen aufzubauen und dann das Array zu durchlaufen, um Kinder für unsere übereinstimmenden Eltern zu finden, und sie im Laufe der Zeit zu unserer Elternliste (p) hinzuzufügen.
Wenn Sie den Prozess der obersten Ebene nicht beenden möchten, tun Sie dies
sub(/[0-9]*/, "", p)
kurz bevor die system () - Zeile sie aus dem Kill-Set entfernen würde.
Denken Sie daran, dass es hier eine Rennbedingung gibt, aber das gilt (soweit ich sehen kann) für alle Lösungen. Es macht das, was ich brauchte, weil das Skript, für das ich es brauchte, nicht viele kurzlebige Kinder schafft.
Eine Übung für den Leser wäre, es zu einer 2-Pass-Schleife zu machen: Senden Sie nach dem ersten Durchgang SIGSTOP an alle Prozesse in der p-Liste, dann eine Schleife, um ps erneut auszuführen, und senden Sie nach dem zweiten Durchgang SIGTERM und dann SIGCONT. Wenn Sie sich nicht für schöne Enden interessieren, könnte der zweite Durchgang einfach SIGKILL sein, nehme ich an.
Wenn Sie die PID der Sache kennen, die Sie töten möchten, können Sie normalerweise von der Sitzungs-ID und von allem in derselben Sitzung ausgehen. Ich würde es noch einmal überprüfen, aber ich habe dies für Skripte verwendet, die rsyncs in Schleifen starten, die ich sterben möchte, und keine andere (wegen der Schleife) starten, als würde ich nur rsync töten.
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))
Wenn Sie die PID nicht kennen, können Sie immer noch mehr nisten
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))
ps -o pid= --ppid $PPID | xargs kill -9
kill -9
wirklich.
kill -15
hilft es nicht.
In sh listet der Befehl jobs die Hintergrundprozesse auf. In einigen Fällen ist es möglicherweise besser, zuerst den neuesten Prozess abzubrechen, z. B. hat der ältere einen gemeinsam genutzten Socket erstellt. In diesen Fällen sortieren Sie die PIDs in umgekehrter Reihenfolge. Manchmal möchten Sie einen Moment warten, bis die Jobs etwas auf die Festplatte oder ähnliches geschrieben haben, bevor sie beendet werden.
Und töte nicht, wenn du nicht musst!
for SIGNAL in TERM KILL; do
for CHILD in $(jobs -s|sort -r); do
kill -s $SIGNAL $CHILD
sleep $MOMENT
done
done
Untergeordneten Prozess im Shell-Skript beenden:
Oft müssen wir untergeordnete Prozesse beenden, die aus irgendeinem Grund gehängt oder blockiert werden. z.B. Problem mit der FTP-Verbindung.
Es gibt zwei Ansätze:
1) Erstellen eines separaten neuen Elternteils für jedes Kind, das den untergeordneten Prozess überwacht und beendet, sobald das Zeitlimit erreicht ist.
Erstellen Sie test.sh wie folgt:
#!/bin/bash
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
for CMD in ${CMDs[*]}; do
(sleep 10 & PID=$!; echo "Started $CMD => $PID"; sleep 5; echo "Killing $CMD => $PID"; kill $PID; echo "$CMD Completed.") &
done
exit;
und beobachten Sie Prozesse, die in einem anderen Terminal den Namen 'test' haben, mit dem folgenden Befehl.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Das obige Skript erstellt 4 neue untergeordnete Prozesse und deren Eltern. Jeder untergeordnete Prozess wird 10 Sekunden lang ausgeführt. Sobald jedoch eine Zeitüberschreitung von 5 Sekunden erreicht ist, werden diese Kinder durch ihre jeweiligen übergeordneten Prozesse getötet. Das Kind kann die Ausführung also nicht abschließen (10 Sekunden). Spielen Sie mit diesen Timings (Schalter 10 und 5), um ein anderes Verhalten zu sehen. In diesem Fall beendet das Kind die Ausführung in 5 Sekunden, bevor das Zeitlimit von 10 Sekunden erreicht ist.
2) Lassen Sie den aktuellen übergeordneten Prozess überwachen und beenden, sobald das Timeout erreicht ist. Dadurch wird kein separates übergeordnetes Element erstellt, um jedes untergeordnete Element zu überwachen. Außerdem können Sie alle untergeordneten Prozesse innerhalb desselben übergeordneten Prozesses ordnungsgemäß verwalten.
Erstellen Sie test.sh wie folgt:
#!/bin/bash
declare -A CPIDs;
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
CMD_TIME=15;
for CMD in ${CMDs[*]}; do
(echo "Started..$CMD"; sleep $CMD_TIME; echo "$CMD Done";) &
CPIDs[$!]="$RN";
sleep 1;
done
GPID=$(ps -o pgid= $$);
CNT_TIME_OUT=10;
CNT=0;
while (true); do
declare -A TMP_CPIDs;
for PID in "${!CPIDs[@]}"; do
echo "Checking "${CPIDs[$PID]}"=>"$PID;
if ps -p $PID > /dev/null ; then
echo "-->"${CPIDs[$PID]}"=>"$PID" is running..";
TMP_CPIDs[$PID]=${CPIDs[$PID]};
else
echo "-->"${CPIDs[$PID]}"=>"$PID" is completed.";
fi
done
if [ ${#TMP_CPIDs[@]} == 0 ]; then
echo "All commands completed.";
break;
else
unset CPIDs;
declare -A CPIDs;
for PID in "${!TMP_CPIDs[@]}"; do
CPIDs[$PID]=${TMP_CPIDs[$PID]};
done
unset TMP_CPIDs;
if [ $CNT -gt $CNT_TIME_OUT ]; then
echo ${CPIDs[@]}"PIDs not reponding. Timeout reached $CNT sec. killing all childern with GPID $GPID..";
kill -- -$GPID;
fi
fi
CNT=$((CNT+1));
echo "waiting since $b secs..";
sleep 1;
done
exit;
und beobachten Sie Prozesse, die in einem anderen Terminal den Namen 'test' haben, mit dem folgenden Befehl.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Das obige Skript erstellt 4 neue untergeordnete Prozesse. Wir speichern Pids aller untergeordneten Prozesse und durchlaufen sie, um zu überprüfen, ob sie ausgeführt wurden oder noch ausgeführt werden. Der untergeordnete Prozess wird bis zur CMD_TIME-Zeit ausgeführt. Wenn das Zeitlimit für CNT_TIME_OUT erreicht ist, werden alle Kinder vom übergeordneten Prozess getötet. Sie können das Timing wechseln und mit dem Skript herumspielen, um das Verhalten zu sehen. Ein Nachteil dieses Ansatzes besteht darin, dass die Gruppen-ID zum Töten aller untergeordneten Bäume verwendet wird. Der übergeordnete Prozess selbst gehört jedoch zur selben Gruppe, sodass er auch getötet wird.
Möglicherweise müssen Sie dem übergeordneten Prozess eine andere Gruppen-ID zuweisen, wenn Sie nicht möchten, dass das übergeordnete Element getötet wird.
Weitere Details finden Sie hier,
Dieses Skript funktioniert auch:
#/bin/sh
while true
do
echo "Enter parent process id [type quit for exit]"
read ppid
if [ $ppid -eq "quit" -o $ppid -eq "QUIT" ];then
exit 0
fi
for i in `ps -ef| awk '$3 == '$ppid' { print $2 }'`
do
echo killing $i
kill $i
done
done
Ich weiß, dass das alt ist, aber das ist die bessere Lösung, die ich gefunden habe:
killtree() {
for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
echo Terminating: $p
kill $p
done
}