"Trap ... INT TERM EXIT" è davvero necessario?


63

Molti esempi da traputilizzare trap ... INT TERM EXITper le attività di pulizia. Ma è davvero necessario elencare tutti e tre i sigspec?

Il manuale dice:

Se un SIGNAL_SPEC è EXIT (0) ARG viene eseguito all'uscita dalla shell.

che credo valga se lo script è finito normalmente o è finito perché ha ricevuto SIGINTo SIGTERM. Un esperimento conferma anche la mia convinzione:

$ cat ./trap-exit
#!/bin/bash
trap 'echo TRAP' EXIT
sleep 3
$ ./trap-exit & sleep 1; kill -INT %1
[1] 759
TRAP
[1]+  Interrupt               ./trap-exit
$ ./trap-exit & sleep 1; kill -TERM %1
[1] 773
TRAP
[1]+  Terminated              ./trap-exit

Allora perché tanti esempi elencano tutti INT TERM EXIT? O mi sono perso qualcosa e c'è un caso in cui una suola EXITmancherebbe?


3
Inoltre, tieni presente che con una specifica come INT TERM EXITil codice di pulizia viene eseguito due volte quando SIGTERMo SIGINTviene ricevuto.
maxschlepzig,

Risposte:


19

Le specifiche POSIX non dicono molto sulle condizioni che portano all'esecuzione della trap EXIT, ma solo su come deve essere il suo ambiente quando viene eseguito.

Nel guscio di cenere di Busybox, il test di uscita trappola non fa eco a "TRAP" prima di uscire a causa di SIGINT o SIGTERM. Sospetto che esistano altre conchiglie che potrebbero non funzionare in questo modo.

# /tmp/test.sh & sleep 1; kill -INT %1
# 
[1]+  Interrupt                  /tmp/test.sh
# 
# 
# /tmp/test.sh & sleep 1; kill -TERM %1
# 
[1]+  Terminated                 /tmp/test.sh
# 

3
dashinoltre non intrappola proprio EXITquando riceve SIGINT/SIGTERM.
maxschlepzig,

4
zshanche - quindi, forse bashè l'unica shell in cui EXITanche i segnali di corrispondenza.
maxschlepzig,

@maxschlepzig zshnon intrappola EXITquando riceve INT, ma lo fa quando riceve TERM. EDIT: Ho appena notato quanti anni aveva ...
JoL

27

Sì, c'è differenza.

Questo script verrà chiuso quando si preme Enter, o lo si invia SIGINTo SIGTERM:

trap '' EXIT
echo ' --- press ENTER to close --- '
read response

Questo script verrà chiuso quando si preme Enter:

trap '' EXIT INT TERM
echo ' --- press ENTER to close --- '
read response

* Testato in sh , Bash e Zsh . (non funziona più in sh quando si aggiunge un comando per l'esecuzione di trap)


C'è anche quello che ha detto @Shawn: Ash e Dash non intrappolano i segnali EXIT.

Quindi, per gestire i segnali in modo robusto, è meglio evitare del EXITtutto il trapping e utilizzare qualcosa del genere:

cleanup() {
    echo "Cleaning stuff up..."
    exit
}

trap cleanup INT TERM
echo ' --- press ENTER to close --- '
read var
cleanup

1
La soluzione con cleanup fa la cosa giusta - molto elegante! È diventato un linguaggio per i miei script bash con le mktempchiamate.
Bjoern Dahlgren,

2
È exitnecessario in cleanup?
jarno,

3
Questo non funziona se nel tuo codice sono presenti errori di shellscript che ne causano l'uscita prematura.
ijw

2
@ijw: in Bash e Ksh, puoi intercettare ERRper gestirlo, ma non è portatile .
Zaz,

5
Questa soluzione non è affidabile quando viene chiamata da un'altra shell. Non gestisce l' attesa all'uscita cooperativa ; vorrai trap - INT TERM; kill -2 $$come ultima riga di cleanup, per dire alla shell genitore che è uscita prematuramente. Se una shell padre foobar.sh chiama il tuo script (foo.sh) e quindi chiama bar.sh, non vuoi che bar.sh venga eseguito se INT / TERM viene inviato a foo.sh. trap cleanup EXITgestirà automaticamente questa propagazione, quindi è l'IMO il più robusto. Significa anche che non dovresti chiamare cleanupalla fine dello script.
Nicholas Pipitone,

12

Perfeziona l'ultima risposta, perché ha dei problemi:

# Our general exit handler
cleanup() {
    err=$?
    echo "Cleaning stuff up..."
    trap '' EXIT INT TERM
    exit $err 
}
sig_cleanup() {
    trap '' EXIT # some shells will call EXIT after the INT handler
    false # sets $?
    cleanup
}
trap cleanup EXIT
trap sig_cleanup INT QUIT TERM

Punti sopra:

I gestori INT e TERM non mi abbandonano quando eseguo il test: gestiscono l'errore, quindi la shell ritorna all'uscita (e questo non è troppo sorprendente). Quindi mi assicuro che il cleanup esca in seguito, e nel caso dei segnali utilizzi sempre un codice di errore (e nell'altro caso di un'uscita normale, preservi il codice di errore).

Con bash, sembra che uscire dal gestore INT chiami anche il gestore EXIT, quindi estraggo il gestore di uscita e lo chiamo io stesso (che funzionerà in qualsiasi shell indipendentemente dal comportamento).

Trappola exit perché gli script della shell possono uscire prima che raggiungano il fondo: errori di sintassi, set -e e un ritorno diverso da zero, semplicemente chiamando exit. Non puoi fare affidamento sul fatto che uno shellscript arrivi in ​​fondo.

SIGQUIT è Ctrl- \ se non l'hai mai provato. Ti dà un bonus coredump. Quindi penso che valga la pena intrappolare, anche se è un po 'oscuro.

L'esperienza passata dice che se (come me) premete sempre Ctrl-C più volte, a volte lo catturerete a metà della parte di pulizia dello script della shell, quindi questo funziona ma non sempre perfettamente come vorreste.


2
Il chiamante sarebbe solo ottenere 1 come il codice di uscita, non importa quale sia il segnale causato l'uscita, mentre withour trapil chiamante otterrebbe 130 per SIGINT, 143 per SIGTERM, ecc Quindi vorrei cogliere e trasmettere il codice di uscita corretto come: sig_cleanup() { err=$?; trap '' EXIT; (exit $err); cleanup; }.
musiphil

2
Potete chiarire lo scopo della trap '' EXIT INT TERMfunzione di pulizia? Questo serve a prevenire l'interruzione accidentale da parte dell'utente della pulizia menzionata nell'ultimo paragrafo? Il EXITridondante non è ?
Sei
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.