o ERR
trap não é para executar o código quando o próprio shell sai com um código de erro diferente de zero, mas quando qualquer comando executado pelo shell que não faz parte de uma condição (como em if cmd...
ou cmd || ...
...) sai com um status de saída diferente de zero (as mesmas condições que fazem com que set -e
saia do shell).
Se você quiser executar o código na saída do shell com status de saída diferente de zero, adicione uma interceptação em EXIT
e verifique $?
there:
trap '[ "$?" -eq 0 ] || echo hi' EXIT
Note, entretanto, que após um sinal aprisionado, tanto o sinal trap como o EXIT serão executados, então você pode querer fazer assim:
unset killed_by
trap 'killed_by=INT;exit' INT
trap 'killed_by=TERM;exit' TERM
trap '
ret=$?
if [ -n "$killed_by" ]; then
echo >&2 "Ouch! Killed by $killed_by"
exit 1
elif [ "$ret" -ne 0 ]; then
echo >&2 "Died with error code $ret"
fi' EXIT
Ou para usar o status de saída como $((signum + 128))
nos sinais:
for sig in INT TERM HUP; do
trap "exit $((128 + $(kill -l "$sig")))" "$sig"
done
trap '
ret=$?
[ "$ret" -eq 0 ] || echo >&2 "Bye: $ret"' EXIT
Note, entretanto, que sair normalmente no SIGINT ou SIGQUIT tem efeitos colaterais potencialmente irritantes quando o processo pai é um shell como bash
que implementa o espera e saída cooperativa manuseio da interrupção do terminal. Então, você pode querer se certificar de se matar com o mesmo sinal para relatar ao seu pai que você foi de fato interrompido, e que ele deveria considerar sair de si mesmo também se ele recebeu um SIGINT / SIGQUIT.
unset killed_by
for sig in INT QUIT TERM HUP; do
trap "exit $((128 + $(kill -l "$sig"))); killed_by=$sig" "$sig"
done
trap '
ret=$?
[ "$ret" -eq 0 ] || echo >&2 "Bye: $ret"
if [ -n "$killed_by" ]; then
trap - "$killed_by" # reset handler
# ulimit -c 0 # possibly disable core dumps
kill -s "$killed_by" "$$"
else
exec "$ret"
fi' EXIT
Se você quiser que a armadilha ERR
seja disparada, basta executar um comando com um status de saída diferente de zero, como false
ou test
.