Я хочу поднять ошибку в сценарии Bash с сообщением "Test cases Failed !!!". Как это сделать в Bash?
Например:
if [ condition ]; then
raise error "Test cases failed !!!"
fi
Я хочу поднять ошибку в сценарии Bash с сообщением "Test cases Failed !!!". Как это сделать в Bash?
Например:
if [ condition ]; then
raise error "Test cases failed !!!"
fi
Это зависит от того, где вы хотите сохранить сообщение об ошибке.
Вы можете сделать следующее:
echo "Error!" > logfile.log
exit 125
Или следующее:
echo "Error!" 1>&2
exit 64
Когда вы вызываете исключение, вы останавливаете выполнение программы.
Вы также можете использовать что-то вроде exit xxx
, где xxx
- это код ошибки, который вы можете вернуть в операционную систему (от 0 до 255). Здесь 125
и 64
- это просто случайные коды, с которыми вы можете выйти. Когда вам нужно указать ОС, что программа остановилась ненормально (например, произошла ошибка), вам необходимо передать ненулевой код выхода на exit
.
Как указано @chepner , вы можете сделать exit 1
, что будет означать неуказанную ошибку.
Если ваш тестовый экземпляр возвращает ненулевой код для неудачных тестов, вы можете просто написать:
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
которые проверяют коды выхода для ошибок__FILE__
, __LINE__
в BashЕсть еще несколько способов, с помощью которых вы можете подойти к этой проблеме. Предполагая, что одно из ваших требований - запустить скрипт/функцию оболочки, содержащую несколько команд оболочки, и проверить, успешно ли скрипт, и выбросить ошибки в случае сбоев.
Команды оболочки обычно полагаются на возвращаемые коды выхода, чтобы позволить оболочке узнать, было ли это успешным или неудачным из-за непредвиденных событий.
Итак, что вы хотите сделать, приходится на эти две категории
В зависимости от того, какой из них вы хотите сделать, доступны опции оболочки. В первом случае оболочка предоставляет опцию с помощью 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)
Здесь простая ловушка, которая печатает последний аргумент того, что не удалось выполнить в 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
не очень хороша (таким образом, сахар), и другие вещи, сбивающие ловушку, могут выглядеть немного глупо. Тем не менее, мне нравится этот метод.
Мне часто бывает полезно написать функцию для обработки сообщений об ошибках, чтобы код был более чистым в целом.
# 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"
У вас есть 2 варианта: перенаправить вывод сценария в файл, ввести файл журнала в скрипт и
Здесь вы предполагаете, что скрипт выводит всю необходимую информацию, включая предупреждения и сообщения об ошибках. Затем вы можете перенаправить вывод в файл по вашему выбору.
./runTests &> output.log
Вышеупомянутая команда перенаправляет как стандартный вывод, так и вывод ошибки в файл журнала.
Используя этот подход, вам не нужно вводить файл журнала в скрипт, и поэтому логика немного проще.
В вашем скрипте добавьте файл журнала либо с помощью жесткого кодирования:
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. Кроме того, если вы создали большой скрипт с несколькими компонентами, вы можете захотеть зарегистрировать каждую часть по-другому, а второй - ваш единственный вариант.