2011-12-02 13 views
20

Ho uno script di bash per verificare come un server funziona sotto carico.Come posso terminare tutti i processi di subshell?

num=1 
if [ $# -gt 0 ]; then 
    num=$1 
fi 
for i in {1 .. $num}; do 
    (while true; do 
     { time curl --silent 'http://localhost'; } 2>&1 | grep real 
    done) & 
done   

wait 

quando ho colpito Ctrl-C, le uscite principali del processo, ma gli anelli di fondo continuare a correre. Come faccio a farli uscire tutti? O c'è un modo migliore di generare un numero configurabile di cicli logici in esecuzione in parallelo?

risposta

33

Ecco una soluzione più semplice - basta aggiungere la seguente riga nella parte superiore dello script:

trap "kill 0" SIGINT 

Uccidere 0 invia il segnale a tutti i processi nel gruppo processo corrente.

+0

Sembra bello e pulito, ma non capisco come vengono gestiti i gruppi di processi. È garantito che tutti i processi in background che sto generando e nessun altro siano nello stesso gruppo di processi dello script? – ykaganovich

+0

Sì, questo è il comportamento predefinito per i gruppi di processi. A meno che tu non abbia scritto un codice che fa esplicitamente una chiamata di sistema per cambiare un gruppo del processo, starai bene. –

+0

@RussellDavis Questo è così pulito e funziona molto bene. Ho dovuto aggiungere trap a tutti gli script di shell che ho generato dallo script master per farlo funzionare. – nograpes

2

È necessario utilizzare job control, che, purtroppo, è un po 'complicato. Se questi sono gli unici processi in background che ci si aspetta sarà in esecuzione, è possibile eseguire un comando come questo:

jobs \ 
    | perl -ne 'print "$1\n" if m/^\[(\d+)\][+-]? +Running/;' \ 
    | while read -r ; do kill %"$REPLY" ; done 

jobs Stampa un elenco di tutti i processi attivi (in esecuzione di posti di lavoro, più di recente finito o lavori terminati), in un formato simile a questo:

[1] Running     sleep 10 & 
[2] Running     sleep 10 & 
[3] Running     sleep 10 & 
[4] Running     sleep 10 & 
[5] Running     sleep 10 & 
[6] Running     sleep 10 & 
[7] Running     sleep 10 & 
[8] Running     sleep 10 & 
[9]- Running     sleep 10 & 
[10]+ Running     sleep 10 & 

(. Questi sono posti di lavoro che ho lanciato eseguendo for i in {1..10} ; do sleep 10 & done)

perl -ne ... sono io utilizzando Perl per estrarre i numeri di lavoro dei processi in esecuzione; puoi ovviamente usare uno strumento diverso se preferisci. Potrebbe essere necessario modificare questo script se il tuo jobs ha un formato di output diverso; ma l'output sopra riportato è anche su Cygwin, quindi è molto probabilmente identico al tuo.

read -r legge una riga "raw" dall'input standard e la salva nella variabile $REPLY. kill %"$REPLY" sarà qualcosa di simile a kill %1, che "uccide" (invia un segnale di interruzione a) il numero di posti di lavoro 1. (Da non confondere con kill 1, che ucciderebbe processo numero 1.) Insieme, while read -r ; do kill %"$REPLY" ; done passa attraverso ogni numero di posti di lavoro stampato da lo script Perl e lo uccide.

A proposito, la tua for i in {1 .. $num} non farà quello che ti aspetti, in quanto espansione delle parentesi graffe è gestito prima espansione dei parametri, in modo da quello che hai è equivalente a for i in "{1" .. "$num}". (E non si può avere lo spazio bianco all'interno dell'espansione coppia, comunque.) Sfortunatamente, non conosco un'alternativa pulita; Penso che devi fare qualcosa come for i in $(bash -c "{1..$num}"), oppure passare a un aritmetico for -loop o whatnot.

Inoltre, non è necessario avvolgere il ciclo while tra parentesi; & causa già l'esecuzione del lavoro in una subshell.

+0

Grazie per i suggerimenti, e soprattutto grazie per BTW punte. Non sono un esperto di bash, quindi lo scrivo su google. – ykaganovich

+0

Prego! So esattamente cosa intendi. Nemmeno io sono un esperto di Bash e sono stato sulla stessa barca fino a circa un anno fa, quando ho trovato il manuale di riferimento di Bash (collegato alla mia risposta). Ha completamente cambiato la mia vita, o almeno la parte di Bash. :-P – ruakh

0

Ecco la mia soluzione finale. Sto tenendo traccia degli ID del processo subshell usando una variabile array e intrappolando il segnale Ctrl-C per ucciderli.

declare -a subs #array of subshell pids 

function kill_subs() { 
    for pid in ${subs[@]}; do 
     kill $pid 
    done 
    exit 0 
} 

num=1 if [ $# -gt 0 ]; then 
    num=$1 fi 

for ((i=0;i < $num; i++)); do 
    while true; do 
     { time curl --silent 'http://localhost'; } 2>&1 | grep real 
    done & 

    subs[$i]=$! #grab the pid of the subshell 
done 

trap kill_subs 1 2 15 

wait 
2

Un modo per uccidere sottoshell, ma non auto:

kill $(jobs -p) 
Problemi correlati