Capture INT e ERR, mas o callback foi executado várias vezes

3

Estou capturando o INT e o ERR com o seguinte código

set -ex -o pipefail

dest=$(mktemp -d)
cd "$dest"

trap "echo; echo Clean up; rm -rf $dest" INT ERR
sleep 9999

Quando pressiono ^C , o retorno de chamada de limpeza é executado várias vezes

++ echo Clean up
Clean up
++ rm -rf /tmp/tmp.KYXL110516
++ echo

++ echo Clean up
Clean up
++ rm -rf /tmp/tmp.KYXL110516

Esse comportamento é esperado? É possível executá-lo apenas uma vez?

    
por daisy 03.05.2018 / 17:31

2 respostas

3

Você está recebendo os sinais INT e ERR; SIGINT é entregue para sleep , que sai com um código de retorno diferente de zero. O código de retorno diferente de zero dispara a armadilha para o SIGERR.

If a sigspec is ERR, the command arg is executed whenever a pipeline (which may consist of a single simple command), a list, or a compound command returns a non-zero exit status...

Um exemplo, para ver as armadilhas separadas:

set -ex -o pipefail
trap "echo Clean up for INT" INT
trap "echo Clean up for ERR" ERR
sleep 9999

Executando, depois Control-C:

+ trap 'echo Clean up for INT' INT
+ trap 'echo Clean up for ERR' ERR
+ sleep 9999
^C++ echo Clean up for INT
Clean up for INT
++ echo Clean up for ERR
Clean up for ERR

Como para chamar a interceptação somente uma vez, uma opção seria redefinir a interceptação ERR enquanto dentro da interceptação INT :

...
trap "echo Clean up for INT; trap ERR" INT ERR
...

... o que resulta em:

+ trap 'echo Clean up for INT; trap ERR' INT ERR
+ sleep 9999
^C++ echo Clean up for INT
Clean up for INT
++ trap ERR
    
por 03.05.2018 / 18:31
1

Em bash , você pode simplesmente substituir isso por trap "my-cleanup-command" EXIT . Esta armadilha será executada quando o script sair, inclusive se for enviado SIGINT. Na minha opinião, esta é geralmente a abordagem mais elegante ...

Exceto pelo fato de ser específico do bash. Armadilhas EXIT são mencionadas no POSIX, mas o comportamento não é especificado. Em muitos outros shells, o trap EXIT não é executado se o shell sair por causa de um sinal.

link

Às vezes eu realmente odeio scripts de shell:).

Olhando para a resposta vinculada, acho que uma maneira menos específica para garantir que nenhum código seja executado após a primeira armadilha seria sair imediatamente. Parece que há maneiras diferentes de você fazer isso, mas aqui está a mais simples que eu tentaria.

cleanup() {
    echo "Clean up"
    rm -rf "$dest"
}
trap cleanup EXIT
trap "exit 1" INT ERR
    
por 04.05.2018 / 09:40

Tags