Bash scripting - segnali
Bash scripting |
Sommario |
Ogni processo in Unix e Unix-like può ricevere dei segnali da altri processi (se appartengono allo stesso utente oppure da root). Di seguito i principali, tra quelli previsti da POSIX:
- INT : è equivalente a
Ctrl-c
da tastiera, per uno script associato a un terminale, se non è eseguito in background. L'azione di default è la terminazione; - QUIT : è equivalente a
Ctrl-\
da tastiera, per uno script associato a un terminale, se non è eseguito in background. L'azione di default è la terminazione con core dump; - HUP : è equivalente a chiudere il terminale, per uno script associato a un terminale, anche se eseguito in background; o in generale alla morte di un processo padre. Viene inviato automaticamente, e l'azione di default è la terminazione;
- TERM : è il segnale di terminazione di default. L'azione di default è la terminazione;
- KILL : è un segnale di terminazione che non può essere ignorato (si veda
trap
). L'unica azione che sarà eseguita è la sua terminazione immediata; - PIPE : è il segnale ricevuto da un processo che invia l'output a una pipe, quando il processo lettore ha terminato. Viene inviato automaticamente, e l'azione di default è la terminazione;
- ABRT : è il segnale di abort. L'azione di default è la terminazione con core dump;
- STOP : è equivalente a
Ctrl-z
da tastiera, per uno script associato a un terminale non eseguito in background, ma come KILL non può mai essere ignorato (si vedatrap
). L'unica azione che sarà eseguita è la sua interruzione immediata; - CONT : è il segnale per continuare l'esecuzione per un processo interrotto. Se il processo non è interrotto, di default il segnale è ignorato;
- TSTP : è equivalente a Bloc Scor (o
Ctrl-s
; per riprendere:Ctrl-q
), per bloccare l'output. L'azione di default è interrompere il processo; - CHLD : è il segnale che un processo figlio è terminato. Viene inviato automaticamente, e di default è ignorato;
- WINCH : è il segnale che la finestra del terminale è stata ridimensionata. Viene inviato automaticamente, e di default è ignorato;
- USR1 : è un segnale lasciato da definire all'utente. L'azione di default è la terminazione;
- USR2 : è un segnale lasciato da definire all'utente. L'azione di default è la terminazione.
Il core dump è un dump dello stato (o una sua parte) del processo terminato, a scopo di debug. Può essere limitato e anche disattivato interamente. Se non attivo, l'azione eseguita è equivalente a una terminazione senza core dump.
Invio di segnali
Alcuni segnali vengono inviati automaticamente, al sussistere di determinate condizioni, ma possono anche essere inviati esplicitamente con il comando kill
come tutti gli altri.
Sintassi: kill [ -s SIGNAL ] pid
Invia un segnale (di default: TERM) al processo con il PID scelto. Si noti che l'invio è asincrono, ossia kill
non resta in attesa che il processo con PID scelto effettui l'azione associata al segnale. Di conseguenza il comando ha sempre successo, salvo che il PID non esista o non possa ricevere il segnale (per esempio perché non è dello stesso utente).
Cattura dei segnali
Le azioni da intraprendere possono essere modificati con l'istruzione trap
.
Sintassi: trap 'stringa' SIGNAL1 [ ... ]
Cattura i segnali (se possibile, non lo è per KILL e STOP), con il seguente comportamento:
- se la stringa è il solo carattere
-
, le azioni associate alla lista di segnali sono impostate nuovamente come quelle di default per i segnali; - se la stringa è vuota, i segnali verranno ignorati;
- se la stringa è il nome di una funzione, verrà eseguita ogni volta che uno dei segnali viene ricevuto. L'azione normale è sovrascritta, quindi se è necessario preservarla dopo la funzione, dovrà occuparsene la funzione stessa (per esempio di uscire con
exit
dopo aver effettuato la pulizia); - il segnale può essere EXIT, che ha significato solo per questa istruzione, in tal caso la funzione sarà eseguita alla fine dello script, che può essere causata anche da segnali (con l'unica eccezione di KILL), purché lo script non termini con un
exec
seguito da un eseguibile. In POSIX invece la funzione sarebbe eseguita soltanto in caso di uscita normale dello script. In ogni caso la terminazione dello script non è annullata definendo un azione, come invece per i veri segnali.
In caso di esecuzione di una subshell le azioni da intraprendere in seguito alla ricezione di un segnale vengono reimpostate al loro valore di default, ma i segnali ignorati continuano a restare ignorati.
Esempi
- Ignora i segnali di terminazione:
trap '' INT HUP QUIT TERM # segnali standard di terminazione
Nota che KILL non può essere ignorato e nemmeno catturato, quindi in genere è sconsigliabile non prevedere una terminazione non brutale attraverso un segnale.
- Ignora il segnale HUP (equivalente a eseguire lo script con il comando
nohup
), per permettere di avviare lo script in background e sopravvivere anche dopo la chiusura del terminale associato:
trap '' HUP
- Esegue una funzione (
interrupt
) alla ricezione di tutti i segnali (standard) di terminazione, prima di uscire:
interrupt () { # fai qualcosa ... exit 127 # chiusura esplicita NECESSARIA # perché l'azione di default è sovrascritta } ... # esegue interrupt se uno dei segnali è ricevuto # e poi esci (perché specificato in interrupt!) trap 'interrupt' INT HUP QUIT TERM ... exit 0 # non esegue interrupt
- Ignora solo i segnali di terminazione da tastiera, ma esegue una funzione (
interrupt
) con il segnale standard di terminazione:
interrupt () { # fai qualcosa ... exit 127 # chiusura esplicita NECESSARIA # perché l'azione di default è sovrascritta } # ignora terminazione da tastiera trap '' INT QUIT # gestisci altri segnali di terminazione trap 'interrupt' HUP TERM
- Esegue una funzione di pulizia (
clean
) al termine dello script:
clean () { # funzione di pulizia ... # NOTA: niente exit finale per evitare loop infinito # infatti è già richiamata da trap ... EXIT! } ... # la funzione clean è eseguita alla terminazione trap 'clean' EXIT # esci (codice 127), eseguendo clean, alla ricezione di questi segnali # non è necessario in bash, perché è implicito trap 'exit 127' INT HUP QUIT TERM ... if comando; then exec eseguibile # clean NON è eseguita dopo exec e un eseguibile fi ... exit 0 # clean è eseguita anche con exit