“armadilha… INT TERM EXIT” realmente necessário?

46

Muitos exemplos para trap use trap ... INT TERM EXIT para tarefas de limpeza. Mas é realmente necessário listar todos os três sigspecs?

O manual diz:

If a SIGNAL_SPEC is EXIT (0) ARG is executed on exit from the shell.

que eu acredito aplica se o script terminou normalmente ou terminou 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 os INT TERM EXIT ? Ou eu perdi alguma coisa e há algum caso em que um único EXIT sentiria falta?

    
por musiphil 08.12.2012 / 01:06

3 respostas

17

A especificação POSIX não diz muito sobre as condições que resultam na execução da armadilha EXIT , apenas sobre como seu ambiente deve ser quando executado.

No shell de cinzas do Busybox, o teste de saída de armadilha não faz eco à 'TRAP' antes de sair devido a SIGINT ou SIGTERM. Eu suspeito que existem outras conchas na existência que podem não funcionar assim também.

# /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
# 
    
por 08.12.2012 / 15:17
21

Sim, há uma diferença.

Este script sairá quando você pressionar Enter ou enviará SIGINT ou SIGTERM :

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

Este script sairá quando você pressionar Enter :

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

* Testado em sh , Bash e Zsh . (não funciona mais em sh quando você adiciona um comando para o trap ser executado)

Há também o que @Shawn disse: Ash e Dash não interceptam sinais com EXIT .

Portanto, para lidar com sinais de maneira robusta, é melhor evitar o trapping de EXIT e usar algo assim:

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

trap cleanup INT TERM
echo ' --- press ENTER to close --- '
read var
cleanup
    
por 07.08.2014 / 22:40
6

Refinando a última resposta, porque tem problemas:

# 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

Pontos acima:

Os manipuladores INT e TERM não param para mim quando eu testo - eles lidam com o erro e o shell retorna para a saída (e isso não é muito surpreendente). Então eu garanto que a limpeza sai depois, e no caso dos sinais sempre usa um código de erro (e no outro caso de uma saída normal, preserva o código de erro).

Com o bash, parece que sair no manipulador INT também chama o manipulador EXIT, portanto, eu desalojo o manipulador de saída e o chamo sozinho (que funcionará em qualquer shell, independentemente do comportamento).

Eu aprisionei a saída porque os scripts de shell podem sair antes de chegarem à parte inferior - 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 tentou. Obtém um coredump bônus. Então eu 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 pressionar Ctrl-C várias vezes, às vezes você vai pegá-lo no meio da parte de limpeza do seu shell script, então isso funciona, mas nem sempre é tão perfeito quanto você gostaria .

    
por 10.11.2016 / 04:38