Qual è il modo migliore per inviare un segnale a tutti i membri di un gruppo di processi?

Voglio uccidere un intero albero del processo. Qual è il modo migliore per farlo usando i linguaggi di scripting comuni? Sto cercando una soluzione semplice.

Non dici se l’albero che vuoi uccidere è un singolo gruppo di processi. (Questo è spesso il caso se l’albero è il risultato della biforcazione da un avvio del server o da una riga di comando della shell.) È ansible scoprire i gruppi di processi usando GNU ps come segue:

  ps x -o "%p %r %y %x %c " 

Se si tratta di un gruppo di processi che si desidera eliminare, è sufficiente utilizzare il comando kill(1) ma invece di dargli un numero di processo, dargli la negazione del numero del gruppo. Ad esempio, per uccidere ogni processo nel gruppo 5112, utilizzare kill -TERM -- -5112 .

Uccidere tutti i processi appartenenti alla stessa struttura del processo utilizzando l’ ID del gruppo di processi ( PGID )

  • kill -- -$PGID Usa segnale predefinito ( TERM = 15)
  • kill -9 -$PGID Usa il segnale KILL (9)

È ansible recuperare il PGID da qualsiasi Process-ID ( PID ) della stessa struttura del processo

  • kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*') (segnale TERM )
  • kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*') (segnale KILL )

Un ringraziamento speciale a Tanager e Speakus per i contributi sugli spazi rimanenti di $PID e sulla compatibilità con OSX.

Spiegazione

  • kill -9 -"$PGID" => Invia il segnale 9 ( KILL ) a tutti i bambini e nipoti …
  • PGID=$(ps opgid= "$PID") => Recupera l’ ID del gruppo di processi da qualsiasi ID processo dell’albero, non solo l’ ID del genitore del processo . Una variante di ps opgid= $PID è ps -o pgid --no-headers $PID dove pgid può essere sostituito da pgrp .
    Ma:
    • ps inserisce spazi PID quando il PID è inferiore a cinque cifre e allineato a destra come notato da tanager . Puoi usare:
      PGID=$(ps opgid= "$PID" | tr -d ' ')
    • ps da OSX stampa sempre l’header, quindi Speakus propone:
      PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
  • grep -o [0-9]* stampa solo cifre successive (non stampa spazi o intestazioni alfabetiche).

Ulteriori linee di comando

 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) 

Limitazione

  • Come notato da David e Hubert Kario , quando l’ kill viene invocata da un processo appartenente allo stesso albero, kill rischi per uccidersi prima di terminare l’uccisione di tutto l’albero.
  • Pertanto, assicurarsi di eseguire il comando utilizzando un processo con un ID del gruppo di processi diverso.

Lunga storia

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

Esegui l’albero del processo in background usando ‘&’

 > ./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 

Il comando pkill -P $PID non uccide il nipote:

 > 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 

Il comando kill -- -$PGID uccide tutti i processi incluso il nipote.

 > 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 

Conclusione

PGID in questo esempio PID e PGID sono uguali ( 28957 ).
Questo è il motivo per cui inizialmente pensavo che kill -- -$PID fosse sufficiente. Ma nel caso in cui il processo sia spawn all’interno di un Makefile l’ ID di processo è diverso dall’ID di gruppo .

Penso che kill -- -$(ps -o pgid= $PID | grep -o [0-9]*) è il miglior trucco semplice per uccidere un intero albero del processo quando viene chiamato da un ID di gruppo diverso (un altro albero del processo).

 pkill -TERM -P 27888 

Questo ucciderà tutti i processi che hanno l’ID del processo genitore 27888.

O più robusto:

 CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS 

quale programma uccidere 33 secondi dopo e educatamente chiedere ai processi di terminare.

Vedi questa risposta per terminare tutti i discendenti.

Per uccidere un albero del processo in modo ricorsivo, usa killtree ():

 #!/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)  [signal]" exit 1 fi killtree [email protected] 

Il comando rkill dal pacchetto pslist invia il segnale dato (o SIGTERM per impostazione predefinita) al processo specificato e tutti i suoi discendenti:

 rkill [-SIG] pid/name... 

la risposta di Brad è anche ciò che consiglierei, tranne che puoi eliminare completamente awk se usi l’opzione --ppid su ps .

 for child in $(ps -o pid -ax --ppid $PPID) do ....... done 

se sai passare il pid del processo genitore, ecco uno script di shell che dovrebbe funzionare:

 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 

Io uso una versione leggermente modificata di un metodo qui descritto: https://stackoverflow.com/a/5311362/563175

Quindi sembra che:

 kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "` 

dove 24901 è il PID del genitore.

Sembra piuttosto brutto, ma funziona perfettamente.

Versione modificata della risposta di zhigang:

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

Per aggiungere alla risposta di Norman Ramsey, può valere la pena guardare a setsid se si desidera creare un gruppo di processi.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html

La funzione setsid () deve creare una nuova sessione, se il processo chiamante non è un leader del gruppo di processi. Al momento della restituzione, il processo di chiamata sarà il responsabile di sessione di questa nuova sessione, sarà il leader del gruppo di processo di un nuovo gruppo di processi e non avrà alcun terminale di controllo. L’ID del gruppo di processi del processo chiamante deve essere uguale all’ID del processo del processo chiamante. Il processo di chiamata deve essere l’unico processo nel nuovo gruppo di processi e l’unico processo nella nuova sessione.

Che prendo a significare che è ansible creare un gruppo dal processo di partenza. L’ho usato in php per essere in grado di uccidere un intero albero del processo dopo averlo avviato.

Questa potrebbe essere una ctriggers idea. Sarei interessato a commenti.

Non posso commentare (non abbastanza reputazione), quindi sono costretto ad aggiungere una nuova risposta , anche se questa non è davvero una risposta.

C’è un piccolo problema con la risposta altrimenti molto bella e completa data da @olibre il 28 febbraio. L’output di ps opgid= $PID conterrà spazi ps opgid= $PID per un PID più breve di cinque cifre perché ps sta giustificando la colonna (allineare il numeri). All’interno dell’intera riga di comando, questo risulta in un segno negativo, seguito da uno o più spazi, seguito dal gruppo PID. La soluzione semplice consiste nel convogliare ps in tr per rimuovere gli spazi:

 kill -- -$( ps opgid= $PID | tr -d ' ' ) 

Ispirato dal commento di ysth

 kill -- -PGID 

invece di dargli un numero di processo, dargli la negazione del numero del gruppo. Come al solito con quasi tutti i comandi, se vuoi un argomento normale che inizia con a - per non essere interpretato come un interruttore, precedilo con --

La seguente funzione di shell è simile a molte altre risposte, ma funziona sia su Linux che su BSD (OS X, ecc.) Senza dipendenze esterne come 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 } 

È molto facile farlo con Python usando psutil . Basta installare psutil con pip e quindi disporre di una suite completa di strumenti di manipolazione dei processi:

 def killChildren(pid): parent = psutil.Process(pid) for child in parent.get_children(True): if child.is_running(): child.terminate() 

Basato sulla risposta di zhigang, questo evita l’auto-uccisione:

 init_killtree() { local pid=$1 child for child in $(pgrep -P $pid); do init_killtree $child done [ $pid -ne $$ ] && kill -kill $pid } 

Se vuoi uccidere un processo per nome:

 killall -9 -g someprocessname 

o

 pgrep someprocessname | xargs pkill -9 -g 

Questa è la mia versione di uccidere tutti i processi figlio usando lo script di bash. Non usa la ricorsione e dipende dal comando pgrep.

Uso

 killtree.sh PID SIGNAL 

Contenuto di 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//,/ } 

Ecco una variazione della risposta di @ zhigang che fa senza AWK, basandosi solo sulle possibilità di parsing native di Bash:

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

Sembra funzionare bene su Mac e Linux. In situazioni in cui non si può fare affidamento sulla possibilità di gestire gruppi di processi, come quando si scrivono script per testare un pezzo di software che deve essere costruito in più ambienti, questa tecnica di tree-walking è decisamente utile.

Probabilmente è meglio uccidere il genitore prima dei bambini; altrimenti il ​​genitore potrebbe generare nuovamente nuovi figli prima che si uccida. Questi sopravviveranno all’omicidio.

La mia versione di ps è diversa da quella sopra; forse troppo vecchio, quindi lo strano grepping …

Usare uno script di shell invece di una funzione di shell ha molti vantaggi …

Tuttavia, è fondamentalmente l’idea di zhigangs


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

Quanto segue è stato testato su FreeBSD, Linux e MacOS X e dipende solo da pgrep e kill (le versioni ps -o non funzionano sotto BSD). Il primo argomento è il padre pid di cui i bambini devono essere terminati. il secondo argomento è un booleano per determinare se anche il pid genitore deve essere terminato.

 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 

Ciò invierà SIGTERM a qualsiasi processo child / grandchild all’interno di uno script di shell e se SIGTERM non riesce, attenderà 10 secondi e quindi invierà kill.


Risposta precedente:

Quanto segue funziona anche, ma ucciderà la shell stessa su BSD.

 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 

Sviluppo la soluzione di zhigang, xyuri e solidsneck ulteriormente:

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

Questa versione eviterà di uccidere la sua discendenza – che causa un stream di processi figlio nelle soluzioni precedenti.

I processi vengono arrestati correttamente prima che venga determinato l’elenco secondario, in modo che nessun nuovo bambino venga creato o scomparso.

Dopo essere stati uccisi, i lavori fermati devono essere continuati a scomparire dal sistema.

Vecchia domanda, lo so, ma tutte le risposte sembrano continuare a chiamare ps, cosa che non mi piace.

Questa soluzione basata su awk non richiede la ricorsione e chiama solo ps una volta.

 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) }' 

O su una linea singola:

 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)}' 

Fondamentalmente l’idea è che costruiamo un array (a) di parent: child entries, quindi aggiriamo l’array trovando i bambini per i nostri genitori corrispondenti, aggiungendoli alla nostra lista di genitori (p) mentre procediamo.

Se non vuoi uccidere il processo di alto livello, allora fallo

 sub(/[0-9]*/, "", p) 

appena prima che la linea system () lo rimuovesse dal kill set.

Tieni presente che qui c’è una condizione di competizione, ma è vero (per quanto posso vedere) di tutte le soluzioni. Fa ciò di cui avevo bisogno perché la sceneggiatura di cui avevo bisogno non creava molti bambini di breve durata.

Un esercizio per il lettore sarebbe quello di renderlo un ciclo a 2 passaggi: dopo il primo passaggio, inviare SIGSTOP a tutti i processi nell’elenco p, quindi eseguire nuovamente il ciclo per eseguire ps e dopo il secondo passaggio inviare SIGTERM, quindi SIGCONT. Se non ti piacciono i buoni finali, il secondo passaggio potrebbe essere SIGKILL, suppongo.

So che è vecchio, ma questa è la soluzione migliore che ho trovato:

 killtree() { for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do echo Terminating: $p kill $p done } 

Grazie per la tua saggezza, gente. Il mio script stava lasciando alcuni processi figli all’uscita e il suggerimento della negazione rendeva le cose più facili. Ho scritto questa funzione per essere utilizzata in altri script, se necessario:

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

Saluti.

se hai pstree e perl sul tuo sistema, puoi provare questo:

 perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)' 

Se conosci il pid della cosa che vuoi uccidere, di solito puoi passare dall’id della sessione e da tutto nella stessa sessione. Dovrei ricontrollare, ma l’ho usato per gli script che iniziano rsyncs nei loop che voglio morire, e non ne avvio un altro (a causa del ciclo) come farebbe se avessi solo killall’d rsync.

 kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709)) 

Se non conosci il pid, puoi ancora nidificare di più

 kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync ))) 
 ps -o pid= --ppid $PPID | xargs kill -9 

In sh il comando jobs elencherà i processi in background. In alcuni casi, potrebbe essere preferibile eliminare prima il processo più recente, ad esempio il precedente ha creato un socket condiviso. In questi casi ordina i PID in ordine inverso. A volte vuoi aspettare che i lavori scrivano qualcosa su disco o cose del genere prima che si fermino.

E non uccidere se non devi!

 for SIGNAL in TERM KILL; do for CHILD in $(jobs -s|sort -r); do kill -s $SIGNAL $CHILD sleep $MOMENT done done 

Uccisione del processo figlio nello script della shell:

Molte volte abbiamo bisogno di uccidere processi secondari che vengono impiccati o bloccati per qualche motivo. per esempio. Problema di connessione FTP.

Ci sono due approcci,

1) Creare un nuovo genitore separato per ogni bambino che monitorerà e ucciderà il processo figlio una volta raggiunto il timeout.

Creare test.sh come segue,

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

e guarda i processi che stanno avendo il nome di ‘test’ in un altro terminale usando il seguente comando.

 watch -n1 'ps x -o "%p %r %c" | grep "test" ' 

Sopra la sceneggiatura verranno creati 4 nuovi processi figli e i loro genitori. Ogni processo figlio verrà eseguito per 10 secondi. Ma una volta scaduto il tempo di 5 secondi, i rispettivi processi genitore uccideranno quei bambini. Quindi il bambino non sarà in grado di completare l’esecuzione (10 secondi). Gioca attorno a questi tempi (switch 10 e 5) per vedere un altro comportamento. In quel caso il bambino finirà l’esecuzione in 5 secondi prima che raggiunga il timeout di 10 secondi.

2) Consenti al monitoraggio genitore corrente e al processo figlio secondario una volta raggiunto il timeout. Questo non creerà un genitore separato per monitorare ogni bambino. Inoltre è ansible gestire correttamente tutti i processi figlio all’interno dello stesso genitore.

Creare test.sh come segue,

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

e guarda i processi che stanno avendo il nome di ‘test’ in un altro terminale usando il seguente comando.

 watch -n1 'ps x -o "%p %r %c" | grep "test" ' 

Sopra lo script creerà 4 nuovi processi figlio. Stiamo memorizzando i pid di tutti i processi figli e li stiamo ciclicamente controllando se hanno terminato la loro esecuzione o se sono ancora in esecuzione. Il processo figlio verrà eseguito fino all’ora CMD_TIME. Ma se CNT_TIME_OUT raggiunge il timeout, tutti i bambini verranno uccisi dal processo padre. Puoi cambiare i tempi e giocare con lo script per vedere il comportamento. Uno svantaggio di questo approccio è che sta usando l’id del gruppo per uccidere tutti gli alberi dei bambini. Ma il processo genitore stesso appartiene allo stesso gruppo, quindi verrà anche ucciso.

Potrebbe essere necessario assegnare un altro ID di gruppo al processo principale se non si desidera che il genitore venga ucciso.

Maggiori dettagli possono essere trovati qui,

Uccisione del processo figlio nello script della shell

Questo script funziona anche:

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