Como disparar um erro usando o comando Trap

13

Estou usando o Ubuntu 12.04.2. Estou tentando usar o comando "trap" para capturar anormal ou erro no meu script de shell, mas também estou tentando disparar manualmente a saída "Error".

Eu tentei a saída 1, mas ele não dispara o sinal de "Erro".

#!/bin/bash

func()
{
    exit 1
}

trap "echo hi" INT TERM ERR
func

Não sabe ao certo como acionar manualmente o sinal de saída "Erro"?

floresta
fonte

Respostas:

20

a ERRarmadilha não é para executar código quando o próprio shell sai com um código de erro diferente de zero, mas quando qualquer comando executado por esse shell que não faz parte de uma condição (como em if cmd..., ou cmd || ......) sai com um valor diferente de zero status de saída (as mesmas condições que causam set -ea saída do shell).

Se você deseja executar o código na saída do shell com status de saída diferente de zero, adicione uma interceptação EXITe verifique $?lá:

trap '[ "$?" -eq 0 ] || echo hi' EXIT

Observe, porém, que, em um sinal interceptado, a interceptação de sinal e a EXIT seriam executadas, portanto, você pode fazer o seguinte:

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

Observe, porém, que sair normalmente de SIGINT ou SIGQUIT tem efeitos colaterais irritantes em potencial quando o processo pai é um shell como o bashque implementa o tratamento de espera e saída cooperativa da interrupção do terminal. Portanto, você pode querer se matar com o mesmo sinal, a fim de relatar aos seus pais que você foi realmente interrompido e que ele deve considerar sair também se receber 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ê deseja que a ERRarmadilha seja acionada, basta executar um comando com um status de saída diferente de zero, como falseou test.

Stéphane Chazelas
fonte
6

Use retorno, não de saída, para definir o status na saída de uma função (se a função cai-through sem um retorno, o estado é o da última instrução executada.) Se você substituir returnpor exitno exemplo da pergunta, ele vai funcionar como Eu acho que você pretendeu: a armadilha será acionada no pseudo-sinal ERR e 'oi' será impresso. Para considerações adicionais, tente o seguinte:

#!/bin/bash

func()
{
    echo 'in func'
    return 99
    echo 'still in func'
}

trap 'echo "done"' EXIT
trap 'status=$?; echo "error status is $status"; trap - EXIT; exit $status' ERR
func
echo 'returned from func'

Você pode tentar várias modificações, como retornar 0, comentar a interceptação de ERR, não cancelar a interceptação EXIT no manipulador de ERR, não sair do manipulador de ERR ou remover o retorno e remover o retorno e colocar falsecomo a última instrução em func.

Sdenham
fonte