2015-10-20 12 views
6

Ho uno script Bash che esegue un processo in esecuzione prolungata in primo piano. Quando riceve un segnale SIGQUIT, dovrebbe eseguire varie operazioni di pulizia come uccidere se stesso e tutti i suoi processi figli (tramite kill del gruppo di processi ecc.). Uno script minima, che dovrebbe prendere il segnale, è mostrato di seguito (chiamato test_trap.sh):Come utilizzare Trap in modo affidabile utilizzando Bash in esecuzione processi figlio in primo piano

#!/bin/bash 

trap 'echo "TRAP CAUGHT"; exit 1' QUIT # other required signals are omitted for brevity 

echo starting sleep 
sleep 11666 
echo ending sleep 

echo done 

vorrei inviare il segnale SIGHUP al processo dello script test_trap.sh. Tuttavia, l'invio di un SIGHUP a test_trap.sh non attiva l'espressione trap ma solo quando invio il segnale al processo figlio sleep 11666 fa il trap. Qui di seguito è una sessione bash che dimostra questo:

bash-4.1$ test_trap.sh & 
[1] 19633 
bash-4.1$ starting sleep 

bash-4.1$ kill -s SIGQUIT 19633 
bash-4.1$ jobs 
[1]+ Running     test_trap.sh & 
bash-4.1$ ps -ef --forest --cols=10000 | grep '11666\|test_trap.sh' | grep -v grep 
theuser 19633 12227 0 07:40 pts/4 00:00:00    \_ /bin/bash ./test_trap.sh 
theuser 19634 19633 0 07:40 pts/4 00:00:00    | \_ sleep 11666 
bash-4.1$ kill -s SIGQUIT 19634 
bash-4.1$ Quit (core dumped) 
TRAP CAUGHT 

[1]+ Exit 1     test_trap.sh 
bash-4.1$ ps -ef --forest --cols=10000 | grep '11666\|test_trap.sh' | grep -v grep 
bash-4.1$ 

Si noti che il "11666 sleep" è solo un processo rappresentativo. Tale processo può effettivamente essere una subshell interattiva (ad es., bash -i).

Perché il processo padre test_trap.sh non rileva il segnale SIGHUP? Perché la trappola si attiva solo quando è stata segnalata la procedura per sleep 11666?

Non desidero utilizzare SIGKILL non acquisibile poiché è necessario eseguire un assortimento di operazioni di pulitura nell'espressione trap.

Questo script è destinato a essere eseguito su qualsiasi versione abbastanza recente di qualsiasi distribuzione Linux contenente Bash (ad esempio, non Cygwin).

Riferimenti:

  1. killing Parent process along with child process using SIGKILL
  2. Kill bash and child process

risposta

6

bash deve attendere il completamento di sleep prima di poter eseguire il gestore. Una buona soluzione è eseguire sleep sullo sfondo, quindi attendere immediatamente. Mentre sleep non è interrompibile, non lo è wait.

trap 'kill $sleep_pid; echo "TRAP CAUGHT"; exit 1' QUIT 

echo starting sleep 
sleep 11666 & 
sleep_pid=$! 
wait 
echo ending sleep 

echo done 

La registrazione di sleep_pid e di utilizzarlo per uccidere sleep dal gestore sono opzionali.

+1

Devo contrassegnarlo come la risposta da quando hai fatto la mia domanda ed è stato abbastanza utile. Tuttavia, ho detto "Quel processo può essere in realtà una subshell interattiva (ad es. Bash -i)." E avrei dovuto aggiungere "Non voglio mettere il processo rappresentativo in background perché potrebbe essere una shell interattiva". – bgoodr

+0

Ricordare che l'utente della subshell interattiva potrebbe comunque impostare i propri trap per evitare che la shell venga interrotta dal chiamante. – chepner

+0

@bgoodr hai mai trovato una soluzione che non richieda la messa in background del processo? – trex005

1

realtà, bash sta ricevendo il segnale, ma è in uno stato non interrompibile attesa del comando sleep a fine. Quando finisce, bash reagirà al segnale ed eseguirà la trappola.

È possibile sostituire il lungo comando sleep con un ciclo di brevi sleep comandi:

while true 
do 
    sleep 1 
done 

Con questo, se si invia il segnale al processo bash, reagirà non appena il corso di esecuzione sleep comando termina, cioè, al massimo 1 secondo dopo che è stato inviato.

+0

Grazie ma vedere la mia risposta su http://stackoverflow.com/questions/33240221/how-to-use-trap-reliably-using-bash-running-foreground-child-processes#comment54286155_33241062 come indicato per la risposta di @ chepner . Se dormire fosse tutto ciò che volevo fare, allora la tua risposta sarebbe sufficiente, ma questo è solo un processo rappresentativo che mi piacerebbe idealmente permettere di essere in esecuzione in primo piano poiché potrebbe finire per essere un processo interattivo 'bash-i'. – bgoodr

0

Provare con il segnale SIGINT (lo stesso che viene inviato premendo Ctrl + C) anziché SIGKILL. Altri segnali funzionano solo quando bash può elaborare I/O o qualche altra condizione.

Problemi correlati