Muitos exemplos para trap
uso trap ... INT TERM EXIT
em tarefas de limpeza. Mas é realmente necessário listar todos os três tipos de sigs?
O manual diz:
Se um SIGNAL_SPEC for EXIT (0), ARG será executado na saída do shell.
que eu acredito que se aplica se o script terminou normalmente ou porque recebeu SIGINT
ou SIGTERM
. Um experimento também confirma minha crença:
$ 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
Então, por que tantos exemplos listam todos INT TERM EXIT
? Ou eu perdi alguma coisa e há algum caso em que uma solteira EXIT
perca?
INT TERM EXIT
o código de limpeza, é executado duas vezes quandoSIGTERM
ouSIGINT
é recebido.Respostas:
A especificação POSIX não diz muito sobre as condições que resultam na execução da interceptação EXIT, apenas sobre como deve ser o ambiente quando é executado.
No shell de cinzas do Busybox, seu teste de captura e saída não faz eco de 'TRAP' antes de sair devido ao SIGINT ou SIGTERM. Eu suspeitaria que existem outras conchas na existência que podem não funcionar dessa maneira também.
fonte
dash
também não aprisiona apenasEXIT
quando recebeSIGINT/SIGTERM
.zsh
também - portanto, talvezbash
seja o único shell em queEXIT
também corresponda a sinais.zsh
não interceptaEXIT
quando recebeINT
, mas quando recebeTERM
. EDIT: Eu só notei quantos anos isso foi ...Sim, existe uma diferença.
Este script será encerrado quando você pressionar Enterou enviar
SIGINT
ouSIGTERM
:Este script será encerrado quando você pressionar Enter:
* Testado em sh , Bash e Zsh . (não funciona mais no sh quando você adiciona um comando para que a interceptação seja executada)
Há também o que @ Shawn disse: Ash e Dash não capturam sinais
EXIT
.Portanto, para lidar com sinais de maneira robusta, é melhor evitar a interceptação
EXIT
e usar algo como isto:fonte
mktemp
chamadas.exit
necessáriocleanup
?ERR
para lidar com isso, mas não é portátil .trap - INT TERM; kill -2 $$
como a última linha de limpeza, informar ao shell pai que ele saiu prematuramente. Se um shell pai foobar.sh chama seu script (foo.sh) e depois chama bar.sh, você não quer que o bar.sh seja executado se INT / TERM for enviado ao seu foo.sh.trap cleanup EXIT
manipulará essa propagação automaticamente, por isso é IMO a mais robusta. Isso também significa que você não precisaria ligarcleanup
no final do script.Refinando a última resposta, porque há problemas:
Pontos acima:
Os manipuladores INT e TERM não param para mim quando eu testo - eles lidam com o erro e o shell volta a sair (e isso não é muito surpreendente). Portanto, garanto que a limpeza saia depois e, no caso dos sinais, use sempre um código de erro (e, no outro caso de uma saída normal, preserve o código de erro).
Com o bash, parece que sair no manipulador INT também chama o manipulador EXIT, portanto, desmarcador o manipulador de saída e o chamo eu mesmo (que funcionará em qualquer shell, independentemente do comportamento).
Intercepto a saída porque os scripts do shell podem sair antes que eles atinjam o fundo - erros de sintaxe, set -e e um retorno diferente de zero, simplesmente chamando exit. Você não pode confiar em um shellscript chegando ao fundo.
SIGQUIT é Ctrl- \ se você nunca experimentou. Você recebe um bônus de coredump. Então acho que também vale a pena capturar, mesmo que seja um pouco obscuro.
A experiência passada diz que, se você (como eu) sempre pressiona Ctrl-C várias vezes, às vezes consegue capturá-lo no meio da parte da limpeza do seu script de shell, portanto isso funciona, mas nem sempre tão perfeitamente quanto você gostaria.
fonte
trap
o chamador iria receber 130 para SIGINT, 143 para SIGTERM, etc. Assim, gostaria de capturar e passar o código de saída correta como:sig_cleanup() { err=$?; trap '' EXIT; (exit $err); cleanup; }
.trap '' EXIT INT TERM
função de limpeza? Isso evita a interrupção acidental do usuário da limpeza mencionada no último parágrafo? OEXIT
redundante não é ?