Какой ваш любимый метод для обработки ошибок в Bash? Лучший пример обработки ошибок, которые я обнаружил в Интернете, был написан Уильямом Шоттсом-младшим по адресу http://www.linuxcommand.org .
Он предлагает использовать следующую функцию для обработки ошибок в Bash:
#!/bin/bash
# A slicker error handling routine
# I put a variable in my scripts named PROGNAME which
# holds the name of the program being run. You can get this
# value from the first item on the command line ($0).
# Reference: This was copied from <http://www.linuxcommand.org/wss0150.php>
PROGNAME=$(basename $0)
function error_exit
{
# ----------------------------------------------------------------
# Function for exit due to fatal program error
# Accepts 1 argument:
# string containing descriptive error message
# ----------------------------------------------------------------
echo "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2
exit 1
}
# Example call of the error_exit function. Note the inclusion
# of the LINENO environment variable. It contains the current
# line number.
echo "Example of error with line number and message"
error_exit "$LINENO: An error has occurred."
У вас есть лучшая процедура обработки ошибок, которую вы используете в скриптах Bash?
Ответы:
Используйте ловушку!
... затем, когда вы создаете временный файл:
и
$temp_foo
будет удален при выходе, и будет напечатан текущий номер строки. (set -e
также даст вам поведение «выход при ошибке», хотя оно сопряжено с серьезными оговорками и ослабляет предсказуемость и переносимость кода).Вы можете либо позволить вызову прерывания
error
для вас (в этом случае он использует код завершения по умолчанию 1 и никакого сообщения), либо вызвать его самостоятельно и предоставить явные значения; например:выйдет со статусом 2 и выдаст явное сообщение.
источник
Это прекрасное решение. Я просто хотел добавить
как элементарный механизм ошибок. Он немедленно остановит ваш скрипт, если простая команда не удастся. Я думаю, что это должно было быть поведением по умолчанию: так как такие ошибки почти всегда означают что-то неожиданное, не совсем «нормально» продолжать выполнять следующие команды.
источник
set -e
не без ошибок : см. mywiki.wooledge.org/BashFAQ/105 для нескольких.set -o pipefail
set -e
имеет высокое соотношение выгод и затрат.set -e
себя, но ряд других постоянных в irc.freenode.org # bash советуют (в довольно сильных выражениях) против этого. Как минимум, рассматриваемые ошибки должны быть хорошо поняты.Чтение всех ответов на этой странице очень вдохновило меня.
Итак, вот мой совет :
содержимое файла: lib.trap.sh
Пример использования:
содержимое файла: trap-test.sh
Бег:
Вывод:
Как вы можете видеть на скриншоте ниже, выходные данные окрашены, и сообщение об ошибке появляется на используемом языке.
источник
test ${#g_libs[@]} == 0
не POSIX-совместимый (тест POSIX поддерживает=
сравнение строк или-eq
числовые сравнения, но не==
, не говоря уже об отсутствии массивов в POSIX), и если вы не пытаетесь быть POSIX-совместимым, почему в мир вы используетеtest
вообще, а не математический контекст?(( ${#g_libs[@]} == 0 ))
В конце концов, легче читать.case "$(uname)" in Darwin ) stderr_log="${TMPDIR}stderr.log";; Linux ) stderr_log="/dev/shm/stderr.log";; * ) stderr_log="/dev/shm/stderr.log" ;; esac
Эквивалентная альтернатива "set -e"
Это делает значение флага несколько понятнее, чем просто "-e".
Случайное добавление: чтобы временно отключить флаг и вернуться к значению по умолчанию (для продолжения выполнения независимо от кодов выхода), просто используйте
Это исключает правильную обработку ошибок, упомянутую в других ответах, но быстро и эффективно (как bash).
источник
$(foo)
на голой линии, а не простоfoo
обычно неправильно. Зачем продвигать это, приводя в качестве примера?Вдохновленный идеями, представленными здесь, я разработал удобный и понятный способ обработки ошибок в сценариях bash в моем проекте Bash .
Просто получая библиотеку, вы получаете из коробки следующее (то есть она остановит выполнение при любой ошибке, как если бы она использовалась
set -e
благодаряtrap
onERR
и некоторому bash-fu ):Есть некоторые дополнительные функции, которые помогают обрабатывать ошибки, такие как try and catch или throw ключевое слово, которые позволяют вам прервать выполнение в точке, чтобы увидеть обратную трассировку. Кроме того, если терминал поддерживает его, он выплевывает смайлики Powerline, окрашивает части вывода для большой читабельности и подчеркивает метод, который вызвал исключение в контексте строки кода.
Недостатком является то, что он не переносим - код работает на bash, вероятно,> = 4 (но я думаю, что он может быть перенесен с некоторым усилием на bash 3).
Код разделен на несколько файлов для лучшей обработки, но меня вдохновила идея обратного следа из приведенного выше ответа Луки Боррионе .
Чтобы узнать больше или взглянуть на источник, смотрите GitHub:
https://github.com/niieani/bash-oo-framework#error-handling-with-exceptions-and-throw
источник
Я предпочитаю что-то действительно легко назвать. Поэтому я использую то, что выглядит немного сложным, но простым в использовании. Я обычно просто копирую и вставляю приведенный ниже код в мои скрипты. Объяснение следует за кодом.
Я обычно помещаю вызов функции очистки в сторону функции error_exit, но это варьируется от сценария к сценарию, поэтому я не учел его. Ловушки улавливают общие сигналы завершения и следят за тем, чтобы все было очищено. Псевдоним - это то, что делает настоящую магию. Мне нравится проверять все на наличие ошибок. Так что в общем я называю программы "если!" Тип заявления. Вычитая 1 из номера строки, псевдоним скажет мне, где произошла ошибка. Это также очень просто, и в значительной степени идиотское доказательство. Ниже приведен пример (просто замените / bin / false на то, что вы собираетесь вызывать).
источник
$LINENO - 1
. Покажи правильно без этого.false || die "hello death"
Еще одним соображением является код возврата для возврата. Просто "
1
" довольно стандартно, хотя есть несколько зарезервированных кодов выхода, которые использует сам bash , и на той же странице утверждается, что определяемые пользователем коды должны находиться в диапазоне 64-113, чтобы соответствовать стандартам C / C ++.Вы могли бы также рассмотреть подход битового вектора, который
mount
использует для его кодов выхода:OR
- объединение кодов позволяет вашему скрипту сигнализировать о нескольких одновременных ошибках.источник
Я использую следующий код ловушки, он также позволяет отслеживать ошибки через каналы и команды времени
источник
function
Ключевое слово беспричинно POSIX-несовместимое. Подумайте о том, чтобы сделать свое заявление простоerror() {
, безfunction
него.${$?}
должно быть$?
, или${?}
если вы настаиваете на использовании ненужных скобок; внутреннее$
неправильно.Я использовал
перед; я думаю, потому что «выход» для меня почему-то не удался. Вышеуказанные значения по умолчанию кажутся хорошей идеей.
источник
Это помогло мне некоторое время. Он печатает сообщения об ошибках или предупреждения красным цветом, по одной строке на параметр, и допускает необязательный код выхода.
источник
Не уверен, будет ли это полезно для вас, но я изменил некоторые из предложенных здесь функций, чтобы включить в них проверку на наличие ошибки (код выхода из предыдущей команды). На каждую «проверку» я также передаю в качестве параметра «сообщение» о том, что это за ошибка для целей регистрации.
Теперь, чтобы вызвать его в том же скрипте (или в другом, если я его использую
export -f error_exit
), я просто пишу имя функции и передаю сообщение в качестве параметра, например так:Используя это, я смог создать действительно надежный bash-файл для некоторого автоматизированного процесса, и он остановится в случае ошибок и
log.sh
сообщит мне ( сделает это)источник
function
ключевых слов, простоerror_exit() {
.cd /home/myuser/afolder || error_exit "Unable to switch to folder"
?Этот трюк полезен для пропущенных команд или функций. Имя отсутствующей функции (или исполняемого файла) будет передано в $ _
источник
$_
будет доступен в функции так же, как$?
? Я не уверен, что есть какая-либо причина использовать один в функции, но не другой.Эта функция в последнее время довольно хорошо меня обслуживает:
Вы вызываете его, добавляя 0 или последнее возвращаемое значение к имени команды для запуска, так что вы можете объединять команды без необходимости проверять наличие ошибок. С этим, этот блок заявления:
Становится так:
Если какая-либо из команд не выполняется, код ошибки просто передается в конец блока. Я нахожу это полезным, когда вы не хотите, чтобы последующие команды выполнялись, если предыдущая не удалась, но вы также не хотите, чтобы скрипт сразу выходил (например, внутри цикла).
источник
Использование ловушки не всегда вариант. Например, если вы пишете какую-то повторно используемую функцию, которая требует обработки ошибок и которая может быть вызвана из любого скрипта (после получения файла вспомогательными функциями), эта функция не может предполагать время выхода внешнего скрипта, что делает использование ловушек очень сложным. Другим недостатком использования прерываний является плохая компоновка, так как вы рискуете перезаписать предыдущее прерывание, которое может быть установлено ранее в цепочке вызывающих.
Есть небольшая хитрость, которую можно использовать для правильной обработки ошибок без ловушек. Как вы уже знаете из других ответов,
set -e
не работает внутри команд, если вы используете||
оператор после них, даже если вы запускаете их в подоболочке; например, это не сработает:Но
||
оператор необходим для предотвращения возврата из внешней функции перед очисткой. Хитрость заключается в том, чтобы запустить внутреннюю команду в фоновом режиме, а затем сразу же дождаться ее.wait
Встроенный возвращают код завершения внутренней команды, и теперь вы используете||
после тогоwait
, а не внутренней функции, поэтомуset -e
работает должным образом внутри последний:Вот общая функция, основанная на этой идее. Он должен работать во всех POSIX-совместимых оболочках, если вы удаляете
local
ключевые слова, т.е. заменяете всеlocal x=y
простоx=y
:Пример использования:
Выполнение примера:
Единственное, что вам нужно знать при использовании этого метода, это то, что все изменения переменных оболочки, сделанные из команды, которую вы передаете
run
, не будут распространяться на вызывающую функцию, потому что команда выполняется в подоболочке.источник