Подтвердить что ты не робот

Повысить ошибку в скрипте Bash

Я хочу поднять ошибку в сценарии Bash с сообщением "Test cases Failed !!!". Как это сделать в Bash?

Например:

if [ condition ]; then
    raise error "Test cases failed !!!"
fi
4b9b3361

Ответ 1

Это зависит от того, где вы хотите сохранить сообщение об ошибке.

Вы можете сделать следующее:

echo "Error!" > logfile.log
exit 125

Или следующее:

echo "Error!" 1>&2
exit 64

Когда вы вызываете исключение, вы останавливаете выполнение программы.

Вы также можете использовать что-то вроде exit xxx, где xxx - это код ошибки, который вы можете вернуть в операционную систему (от 0 до 255). Здесь 125 и 64 - это просто случайные коды, с которыми вы можете выйти. Когда вам нужно указать ОС, что программа остановилась ненормально (например, произошла ошибка), вам необходимо передать ненулевой код выхода на exit.

Как указано @chepner , вы можете сделать exit 1, что будет означать неуказанную ошибку.

Ответ 2

Основная обработка ошибок

Если ваш тестовый экземпляр возвращает ненулевой код для неудачных тестов, вы можете просто написать:

test_handler test_case_x; test_result=$?
if ((test_result != 0)); then
  printf '%s\n' "Test case x failed" >&2  # write error message to stderr
  exit 1                                  # or exit $test_result
fi

Или даже короче:

if ! test_handler test_case_x; then
  printf '%s\n' "Test case x failed" >&2
  exit 1
fi

Или кратчайший:

test_handler test_case_x || { printf '%s\n' "Test case x failed" >&2; exit 1; }

Чтобы выйти с кодом выхода test_handler:

test_handler test_case_x || { ec=$?; printf '%s\n' "Test case x failed" >&2; exit $ec; }

Расширенная обработка ошибок

Если вы хотите использовать более полный подход, вы можете иметь обработчик ошибок:

exit_if_error() {
  local exit_code=$1
  shift
  [[ $exit_code ]] &&               # do nothing if no error code passed
    ((exit_code != 0)) && {         # do nothing if error code is 0
      printf 'ERROR: %s\n' "[email protected]" >&2 # we can use better logging here
      exit "$exit_code"             # we could also check to make sure
                                    # error code is numeric when passed
    }
}

затем вызовите его после запуска тестового примера:

run_test_case test_case_x
exit_if_error $? "Test case x failed"

или же

run_test_case test_case_x || exit_if_error $? "Test case x failed"

Преимущества использования обработчика ошибок, например exit_if_error следующие:

  • мы можем стандартизировать всю логику обработки ошибок, такую как ведение журнала, печать трассировки стека, уведомление, очистка и т.д., в одном месте
  • заставив обработчик ошибок получить код ошибки в качестве аргумента, мы можем освободить вызывающего из беспорядка блоков if которые проверяют коды выхода для ошибок
  • если у нас есть обработчик сигналов (с использованием ловушки), мы можем вызвать обработчик ошибок оттуда

Похожие сообщения

Ответ 3

Есть еще несколько способов, с помощью которых вы можете подойти к этой проблеме. Предполагая, что одно из ваших требований - запустить скрипт/функцию оболочки, содержащую несколько команд оболочки, и проверить, успешно ли скрипт, и выбросить ошибки в случае сбоев.

Команды оболочки обычно полагаются на возвращаемые коды выхода, чтобы позволить оболочке узнать, было ли это успешным или неудачным из-за непредвиденных событий.

Итак, что вы хотите сделать, приходится на эти две категории

  • выйти с ошибкой
  • выйти и очистить ошибку

В зависимости от того, какой из них вы хотите сделать, доступны опции оболочки. В первом случае оболочка предоставляет опцию с помощью set -e а для второго вы можете сделать trap на EXIT

Должен ли я использовать exit в моем скрипте/функции?

Использование exit обычно повышает читаемость. В определенных процедурах, как только вы знаете ответ, вы хотите немедленно выйти из вызывающей процедуры. Если процедура определена таким образом, что она не требует никакой дополнительной очистки после обнаружения ошибки, а не выхода из нее немедленно означает, что вам нужно написать больше кода.

Поэтому в случаях, если вам необходимо выполнить очистку действий на скрипте, чтобы сделать окончание сценария чистым, предпочтительнее не использовать exit.

Должен ли я использовать set -e для ошибки при выходе?

Нет!

set -e - попытка добавить "автоматическое обнаружение ошибок" в оболочку. Его цель состояла в том, чтобы заставить оболочку прерываться в любое время, когда произошла ошибка, но она имеет множество потенциальных ошибок, например,

  • Команды, которые являются частью теста if, невосприимчивы. В этом примере, если вы ожидаете, что он сломается на test проверке в каталоге, отличном от -e, он не будет, он переходит в условие else

    set -e
    f() { test -d nosuchdir && echo no dir; }
    f
    echo survived
    
  • Команды в конвейере, отличные от последнего, невосприимчивы. В приведенном ниже примере, поскольку последний обработанный (самый правый) код выхода команды считается (cat), и он был успешным. Этого можно избежать, установив set -o pipefail но это все еще оговорка.

    set -e
    somecommand that fails | cat -
    echo survived 
    

Рекомендуется для использования - trap при выходе

Вердикт заключается в том, что вы хотите иметь возможность обрабатывать ошибку вместо слепого выхода вместо использования set -e, использовать trap псевдосигнала ERR.

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

Общая практика заключается в том, что мы определяем обработчик ловушки для предоставления дополнительной отладочной информации о том, какая строка и что является причиной выхода. Помните, что код завершения последней команды, вызвавший сигнал ERR, по-прежнему будет доступен в этой точке.

cleanup() {
    exitcode=$?
    printf 'error condition hit\n' 1>&2
    printf 'exit code returned: %s\n' "$exitcode"
    printf 'the command executing at the time of the error was: %s\n' "$BASH_COMMAND"
    printf 'command present on line: %d' "${BASH_LINENO[0]}"
    # Some more clean up code can be added here before exiting
    exit $exitcode
}

и мы просто используем этот обработчик, как показано ниже поверх сценария, который не работает

trap cleanup ERR

Объединяя это в простой скрипт, содержащий false в строке 15, информацию, которую вы получите как

error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15

trap также предоставляет параметры независимо от ошибки, чтобы просто запустить очистку при завершении оболочки (например, ваш сценарий оболочки завершен), в сигнале EXIT. Вы также можете захватить несколько сигналов одновременно. Список поддерживаемых сигналов для ловушки можно найти на странице trap_p - Linux.

Еще одно замечание заключается в том, чтобы понять, что ни один из предоставленных методов не работает, если вы имеете дело с суб-оболочками, и в этом случае вам может понадобиться добавить собственную обработку ошибок.

  • На под-оболочке с set -e работать не будет. Значение false ограничивается суб-оболочкой и никогда не распространяется на родительскую оболочку. Чтобы сделать обработку ошибок здесь, добавьте свою собственную логику (false) || false (false) || false

    set -e
    (false)
    echo survived
    
  • То же самое происходит и с trap. Логика ниже не будет работать по причинам, упомянутым выше.

    trap 'echo error' ERR
    (false)
    

Ответ 4

Здесь простая ловушка, которая печатает последний аргумент того, что не удалось выполнить в STDERR, сообщает о линии, в которой она не удалось, и выходит из сценария с номером строки в качестве кода выхода. Обратите внимание, что это не всегда отличные идеи, но это демонстрирует некоторые творческие приложения, которые вы могли бы использовать.

trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR

Я поместил это в скрипт с циклом, чтобы проверить его. Я просто проверяю на попадание на некоторые случайные числа; вы можете использовать фактические тесты. Если мне нужно поручиться, я вызываю false (который вызывает ловушку) с сообщением, которое я хочу бросить.

Для разработанных функций вызовите функцию ловушки для обработки. Вы всегда можете использовать оператор case для своего arg ($ _), если вам нужно сделать больше очистки и т.д. Назначьте var для небольшого синтаксического сахара -

trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR
throw=false
raise=false

while :
do x=$(( $RANDOM % 10 ))
   case $x in
   0) $throw "DIVISION BY ZERO" ;;
   3) $raise "MAGIC NUMBER"     ;;
   *) echo got $x               ;;
   esac
done

Пример вывода:

# bash tst
got 2
got 8
DIVISION BY ZERO at 6
# echo $?
6

Очевидно, вы могли бы

runTest1 "Test1 fails" # message not used if it succeeds

Много места для улучшения дизайна.

Обратные обратные связи включают в себя тот факт, что false не очень хороша (таким образом, сахар), и другие вещи, сбивающие ловушку, могут выглядеть немного глупо. Тем не менее, мне нравится этот метод.

Ответ 5

Мне часто бывает полезно написать функцию для обработки сообщений об ошибках, чтобы код был более чистым в целом.

# Usage: die [exit_code] [error message]
die() {
  local code=$? now=$(date +%T.%N)
  if [ "$1" -ge 0 ] 2>/dev/null; then  # assume $1 is an error code if numeric
    code="$1"
    shift
  fi
  echo "$0: ERROR at ${now%???}${1:+: $*}" >&2
  exit $code
}

Это принимает код ошибки из предыдущей команды и использует его как код ошибки по умолчанию при выходе из всего скрипта. Он также отмечает время, с поддержкой микросекунд (GNU date %N - наносекунды, которые мы усекаем до микросекунд позже).

Если первый параметр равен нулю или положительному целому числу, он становится кодом выхода, и мы удаляем его из списка параметров. Затем мы сообщаем сообщение стандартной ошибке с именем сценария, словом "ERROR" и временем (мы используем расширение параметра для усечения наносекунд до микросекунд или для времени, отличного от GNU, для усечения, например, 12:34:56.%N до 12:34:56). После слова "ОШИБКА" добавляется двоеточие и пробел, но только при наличии предоставленного сообщения об ошибке. Наконец, мы выходим из сценария, используя ранее определенный код выхода, запуская любые ловушки как обычно.

Некоторые примеры (предположим, что код живет в script.sh):

if [ condition ]; then die 123 "condition not met"; fi
# exit code 123, message "script.sh: ERROR at 14:58:01.234564: condition not met"

$command |grep -q condition || die 1 "'$command' lacked 'condition'"
# exit code 1, "script.sh: ERROR at 14:58:55.825626: 'foo' lacked 'condition'"

$command || die
# exit code comes from command's, message "script.sh: ERROR at 14:59:15.575089"

Ответ 6

У вас есть 2 варианта: перенаправить вывод сценария в файл, ввести файл журнала в скрипт и

  1. Перенаправление вывода в файл:

Здесь вы предполагаете, что скрипт выводит всю необходимую информацию, включая предупреждения и сообщения об ошибках. Затем вы можете перенаправить вывод в файл по вашему выбору.

./runTests &> output.log

Вышеупомянутая команда перенаправляет как стандартный вывод, так и вывод ошибки в файл журнала.

Используя этот подход, вам не нужно вводить файл журнала в скрипт, и поэтому логика немного проще.

  1. Вставьте файл журнала в скрипт:

В вашем скрипте добавьте файл журнала либо с помощью жесткого кодирования:

logFile='./path/to/log/file.log'

или передавая его по параметру:

logFile="${1}"  # This assumes the first parameter to the script is the log file

Рекомендуется добавить временную метку во время выполнения в файл журнала в верхней части скрипта:

date '+%Y%-m%d-%H%M%S' >> "${logFile}"

Затем вы можете перенаправить свои сообщения об ошибках в файл журнала

if [ condition ]; then
    echo "Test cases failed!!" >> "${logFile}"; 
fi

Это добавит ошибку в файл журнала и продолжит выполнение. Если вы хотите прекратить выполнение при возникновении критических ошибок, вы можете exit из сценария:

if [ condition ]; then
    echo "Test cases failed!!" >> "${logFile}"; 
    # Clean up if needed
    exit 1;
fi

Обратите внимание, что exit 1 указывает, что выполнение программы останавливается из-за неопределенной ошибки. Вы можете настроить это, если хотите.

Используя этот подход, вы можете настроить свои журналы и иметь другой файл журнала для каждого компонента вашего скрипта.


Если у вас относительно небольшой скрипт или вы хотите выполнить какой-либо другой скрипт без его модификации, то более подходящим будет первый подход.

Если вы всегда хотите, чтобы файл журнала находился в одном месте, это лучший вариант для 2. Кроме того, если вы создали большой скрипт с несколькими компонентами, вы можете захотеть зарегистрировать каждую часть по-другому, а второй - ваш единственный вариант.