Я хочу вызвать ошибку в сценарии Bash с сообщением «Test case Failed !!!». Как это сделать в Баше?
Например:
if [ condition ]; then
raise error "Test cases failed !!!"
fi
Я хочу вызвать ошибку в сценарии Bash с сообщением «Test case Failed !!!». Как это сделать в Баше?
Например:
if [ condition ]; then
raise error "Test cases failed !!!"
fi
echo you screwed up at ... | mail -s BUG $bugtrackeremailaddress
?
Ответы:
Это зависит от того, где вы хотите сохранить сообщение об ошибке.
Вы можете сделать следующее:
echo "Error!" > logfile.log
exit 125
Или следующее:
echo "Error!" 1>&2
exit 64
Когда вы вызываете исключение, вы останавливаете выполнение программы.
Вы также можете использовать что-то вроде exit xxx
где xxx
находится код ошибки, который вы, возможно, захотите вернуть в операционную систему (от 0 до 255). Here 125
и 64
просто случайные коды Вы можете выйти с. Если вам необходимо указать ОСА , что программа остановлена неправильно (например , произошла. Ошибка), вы должны передать код выхода ненулевого в exit
.
Как отметил @chepner , вы можете сделать exit 1
, что будет означать неопределенную ошибку .
1>&2
сделает
exit
сам по себе использует статус выхода последней выполненной команды, который может быть 0.
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' "$@" >&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
блоков, которые проверяют коды выхода на наличие ошибокВот полная реализация обработки ошибок и ведения журнала:
https://github.com/codeforester/base/blob/master/lib/stdlib.sh
__FILE__
, __LINE__
в BashЕсть еще пара способов решить эту проблему. Предполагая, что одно из ваших требований - запустить сценарий / функцию оболочки, содержащую несколько команд оболочки, и проверить, успешно ли выполняется сценарий, и выдать ошибки в случае сбоев.
Команды оболочки обычно полагаются на возвращаемые коды выхода, чтобы сообщить оболочке, была ли она успешной или неудачной из-за некоторых неожиданных событий.
Итак, то, что вы хотите делать, относится к этим двум категориям
В зависимости от того, что вы хотите сделать, существуют доступные параметры оболочки. В первом случае оболочка предоставляет возможность, set -e
а во втором вы можете сделатьtrap
наEXIT
exit
в моем скрипте / функции?С помощью exit
целом улучшает читаемость. В некоторых подпрограммах, как только вы знаете ответ, вы хотите немедленно выйти из вызывающей процедуры. Если подпрограмма определена таким образом, что она не требует дополнительной очистки после обнаружения ошибки, то незамедлительный выход означает, что вам нужно написать больше кода.
Поэтому в случаях, если вам нужно выполнить действия по очистке сценария, чтобы завершить завершение сценария, рекомендуется не использовать exit
.
set -e
для ошибки при выходе?Нет!
set -e
была попытка добавить в оболочку "автоматическое обнаружение ошибок". Его цель заключалась в том, чтобы заставить оболочку прерываться каждый раз при возникновении ошибки, но он имеет множество потенциальных ловушек, например,
Команды, которые являются частью теста if, неуязвимы. В этом примере, если вы ожидаете, что он прервется при test
проверке несуществующего каталога, этого не произойдет, он перейдет к условию 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
используйте a 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.1p - Linux.
Еще одна вещь, на которую следует обратить внимание, - это понять, что ни один из предоставленных методов не работает, если вы имеете дело с суб-оболочками, и в этом случае вам может потребоваться добавить свою собственную обработку ошибок.
На суб-оболочке с set -e
не будет работать. 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 в своем аргументе ($ _), если вам нужно выполнить дополнительную очистку и т. Д. Назначьте 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
недостаткам можно отнести тот факт, что это некрасиво (следовательно, сахар), а другие вещи, срабатывающие в ловушке, могут выглядеть немного глупо. Тем не менее, мне нравится этот метод.
У вас есть 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-х. Также, если вы создали большой сценарий с несколькими компонентами, вы можете захотеть регистрировать каждую часть по-разному, и второй подход - ваш единственный вариант.
Я часто считаю полезным написать функцию для обработки сообщений об ошибках, чтобы код в целом был чище.
# 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 %N
- наносекунды, которые позже мы усекаем до микросекунд).
Если первый вариант равен нулю или положительному целому числу, он становится кодом выхода, и мы удаляем его из списка вариантов. Затем мы сообщаем сообщение об ошибке с указанием имени сценария, слова «ERROR» и времени (мы используем расширение параметра для усечения наносекунд до микросекунд или для времени, отличного от GNU, для усечения, например, 12:34:56.%N
до12:34:56
). Двоеточие и пробел добавляются после слова ERROR, но только при наличии сообщения об ошибке. Наконец, мы выходим из скрипта, используя ранее определенный код выхода, запуская любые ловушки как обычно.
Некоторые примеры (предположим, что код живет внутри 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"