Как вызвать ошибку с помощью команды Trap

13

Я использую Ubuntu 12.04.2. Я пытаюсь использовать команду «trap», чтобы зафиксировать ненормальное или ошибку в моем сценарии оболочки, но я также пытаюсь вручную вызвать выход «Error».

Я попытался выйти 1, но он не вызовет сигнал «Ошибка».

#!/bin/bash

func()
{
    exit 1
}

trap "echo hi" INT TERM ERR
func

Не знаете, как вручную вызвать сигнал выхода «Ошибка»?

forestclown
источник

Ответы:

20

ERRловушка не для запуска кода , когда сама оболочка выходит с ненулевым кодом ошибки, но когда любая команда запуска этой оболочки , которая не является частью состояния (например , в if cmd..., или cmd || ......) выходит с ненулевым состояние выхода (те же условия, что и set -eпри выходе из оболочки).

Если вы хотите запускать код при выходе из оболочки с ненулевым статусом выхода, вы должны EXITвместо этого добавить ловушку и проверить $?там:

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

Тем не менее, обратите внимание, что при перехваченном сигнале будут работать как ловушка сигнала, так и ловушка EXIT, поэтому вы можете сделать это следующим образом:

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

Или использовать статус выхода, как $((signum + 128))по сигналам:

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

Однако обратите внимание, что обычный выход из SIGINT или SIGQUIT имеет потенциальные раздражающие побочные эффекты, когда ваш родительский процесс представляет собой оболочку, подобную bashэтой, которая реализует ожидание и совместную обработку выхода терминального прерывания. Таким образом, вы можете захотеть убить себя тем же сигналом, чтобы сообщить родителям о том, что вы действительно прерваны, и что он также должен рассмотреть возможность выхода из себя, если получил 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

Если вы хотите, чтобы ERRловушка сработала, просто запустите команду с ненулевым статусом выхода, например falseили test.

Стефан Шазелас
источник
6

Используйте возвращение, не выход, чтобы установить статус на выходе из функции (если функция падает сквозной без возврата, статус является то , что последнее утверждение выполняется.) Если вы заменяете returnдля exitпримера на вопрос, он будет работать как Я думаю, что вы намеревались: ловушка будет срабатывать на псевдосигнале ERR, и будет напечатано «привет». Для дополнительных соображений попробуйте это:

#!/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'

Вы можете попробовать различные модификации, например вернуть 0, закомментировать ловушку ERR, не отменить ловушку EXIT в обработчике ERR, не выйти из обработчика ERR или удалить возврат и поместить его falseкак последний оператор в func.

sdenham
источник