Estou observando um comportamento estranho ao usar set -e
( errexit
), set -u
( nounset
) junto com as armadilhas ERR e EXIT. Eles parecem relacionados, portanto, colocá-los em uma pergunta parece razoável.
1) set -u
não aciona interceptações de ERR
Código:
#!/bin/bash trap 'echo "ERR (rc: $?)"' ERR set -u echo ${UNSET_VAR}
- Esperado: o trap de erro é chamado, RC! = 0
- Real: o trap de ERR não é chamado, RC == 1
- Nota:
set -e
não altera o resultado
2) O uso set -eu
do código de saída em uma interceptação EXIT é 0 em vez de 1
Código:
#!/bin/bash trap 'echo "EXIT (rc: $?)"' EXIT set -eu echo ${UNSET_VAR}
- Esperado: EXIT trap é chamado, RC == 1
- Real: a armadilha EXIT é chamada, RC == 0
- Nota: Ao usar
set +e
, o RC == 1. O trap EXIT retorna o RC apropriado quando qualquer outro comando gera um erro. - Edit: Existe uma publicação do SO sobre este tópico com um comentário interessante sugerindo que isso pode estar relacionado à versão do Bash que está sendo usada. Testar esse snippet com o Bash 4.3.11 resulta em RC = 1, então é melhor. Infelizmente, a atualização do Bash (de 3.2.51) em todos os hosts não é possível no momento, portanto, temos que apresentar outra solução.
Alguém pode explicar um desses comportamentos?
A pesquisa nesses tópicos não teve muito êxito, o que é bastante surpreendente, dado o número de postagens nas configurações e armadilhas do Bash. Porém, existe um tópico no fórum , mas a conclusão é bastante insatisfatória.
set -e
e set -u
são projetados especificamente para matar um shell com script. Usá-los em condições que podem acionar seu aplicativo matará um shell com script. Não há como contornar isso, exceto para não usá-los e, em vez disso, testar essas condições quando elas se aplicam em uma sequência de código. Então, basicamente, você pode escrever um bom código de shell ou usar set -eu
.
-u
que não acionaria a interceptação de ERR (é um erro, portanto, não deveria acionar a interceptação) ou o código de erro é 0 em vez de 1. último parece ser um bug que já foi corrigido na versão posterior, então é isso. Mas a primeira parte é bastante difícil de entender se você não percebeu que erros na avaliação do shell (expansão de parâmetros) e erros reais nos comandos parecem ser duas coisas diferentes. Para a solução, bem, como você sugeriu, agora estou tentando evitar -eu
e verificar manualmente quando for necessário.
(set -u; : $UNSET_VAR)
e similar. Esse tipo de coisa também pode ser bom - você pode largar muitas de &&
vez em (set -e; mkdir dir; cd dir; touch dirfile)
quando:, se você entender minha tendência. Só que esses são contextos controlados - quando você os define como opções globais, você perde o controle e se torna controlado. Geralmente, existem soluções mais eficientes.
bash
rompeu com o padrão e comecei a colocar armadilhas nas subcascas. A armadilha deve ser executada no mesmo ambiente de onde veio o retorno, masbash
não faz isso há um bom tempo.