Как перехватывать ошибки в сценариях Bash в Linux

Опубликовано: 2022-08-27
Ноутбук Linux с приглашением bash
Фатмавати Ачмад Заэнури/Shutterstock.com

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

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

Обработка ошибок является частью программирования. Даже если вы пишете безупречный код, вы все равно можете столкнуться с ошибками. Среда на вашем компьютере меняется со временем, когда вы устанавливаете и удаляете программное обеспечение, создаете каталоги и выполняете обновления и обновления.

Настройка ошибок проверки параметров в PowerShell
СВЯЗАННЫЕ Ошибки проверки параметров настройки в PowerShell

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

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

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

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

Обнаружение статуса выхода

Команды и программы генерируют значение, которое отправляется в операционную систему при завершении работы. Это называется их статусом выхода. Он имеет нулевое значение, если ошибок не было, или некоторое ненулевое значение, если произошла ошибка.

Мы можем проверить статус выхода — также известный как код возврата — команд, которые использует сценарий, и определить, была ли команда успешной или нет.

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

Скопируйте этот сценарий в редактор и сохраните его в файле с именем «bad_command.sh».

 #!/бин/баш

если ( ! плохая_команда ); тогда
  echo "bad_command помечает ошибку."
  выход 1
фи

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

 chmod +x bad_command.sh 

Делаем скрипт исполняемым с помощью chmod

Когда мы запускаем скрипт, мы видим ожидаемое сообщение об ошибке.

 ./bad_command.sh 

Проверка состояния выхода команды, чтобы определить, произошла ли ошибка

Нет такой команды, как «плохая_команда», и это не имя функции в скрипте. Он не может быть выполнен, поэтому ответ не равен нулю. Если ответ не нулевой (здесь восклицательный знак используется как логический оператор NOT ), выполняется тело оператора if .

В реальном сценарии это может завершить работу сценария, как в нашем примере, или попытаться исправить состояние ошибки.

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

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

 команда_1 || команда_2

Это работает, потому что выполняется OR первая команда, либо вторая. Самая левая команда запускается первой. В случае успеха вторая команда не выполняется. Но если первая команда не удалась, выполняется вторая команда. Таким образом, мы можем структурировать код следующим образом. Это «логическое ИЛИ./Ш».

 #!/бин/баш

error_handler()
{
  echo "Ошибка: ($?) $1"
  выход 1
}

плохая_команда || error_handler "Плохая_команда не выполнена, Строка: ${LINENO}"

Мы определили функцию с именем error_handler . Это распечатывает статус выхода неудачной команды, хранящийся в переменной $? и строка текста, которая передается ему при вызове функции. Это хранится в переменной $1 . Функция завершает скрипт со статусом выхода, равным единице.

Сценарий пытается запустить bad_command , что явно не удается, поэтому команда справа от логического оператора OR , || , выполняется. Это вызывает функцию error_handler и передает строку, которая называет команду, которая завершилась ошибкой, и содержит номер строки неудачной команды.

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

 ./логический-или.ш
 эхо $? 

Использование логического оператора ИЛИ для вызова обработчика ошибок в скрипте

Наша небольшая функция error_handler предоставляет статус завершения попытки запуска bad_command , имя команды и номер строки. Это полезная информация при отладке скрипта.

Выходной статус скрипта один. Статус выхода 127, о котором сообщает error_handler , означает «команда не найдена». Если бы мы захотели, мы могли бы использовать это как статус выхода скрипта, передав его команде exit .

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

 выход_код=$?

если [ $exit_code -eq 1 ]; тогда
  эхо "Операция не разрешена"

Элиф [$exit_code -eq 2]; тогда
  echo "Неправильное использование встроенных функций оболочки"
.
.
.
Элиф [ $status -eq 128 ]; тогда
  эхо "Неверный аргумент"
фи

Использование набора для принудительного выхода

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

Для этого используйте команду set с параметром -e (ошибка). Это указывает сценарию на выход всякий раз, когда команда терпит неудачу или возвращает код выхода больше нуля. Кроме того, использование параметра -E обеспечивает обнаружение и перехват ошибок в функциях оболочки.

Как использовать set и pipefail в сценариях Bash в Linux
СВЯЗАННЫЕ Как использовать set и pipefail в сценариях Bash в Linux

Чтобы также перехватывать неинициализированные переменные, добавьте параметр -u (не установлен). Чтобы убедиться, что ошибки обнаруживаются в передаваемых последовательностях, добавьте параметр -o pipefail . Без этого статус выхода переданной последовательности команд является статусом выхода последней команды в последовательности. Неудачная команда в середине переданной последовательности не будет обнаружена. Опция -o pipefail должна быть в списке опций.

Последовательность, которую нужно добавить в начало вашего скрипта:

 установить -Eeuo pipefail

Вот короткий скрипт под названием «unset-var.sh» с неустановленной переменной.

 #!/бин/баш

set -Eeou pipefail

эхо "$ unset_variable"

echo "Мы видим эту строку?"

Когда мы запускаем скрипт, unset_variable распознается как неинициализированная переменная, и скрипт завершается.

 ./unset-var.sh 

Использование команды set в скрипте для завершения скрипта в случае возникновения ошибки

Вторая команда echo никогда не выполняется.

Использование ловушки с ошибками

Команда ловушки Bash позволяет указать команду или функцию, которые должны вызываться при получении определенного сигнала. Обычно это используется для перехвата таких сигналов, как SIGINT , который возникает при нажатии комбинации клавиш Ctrl+C. Этот скрипт называется «sigint.sh».

 #!/бин/баш

trap "echo -e '\nЗавершено нажатием Ctrl+c'; выход" SIGINT

счетчик=0

пока правда
делать 
  echo "Номер цикла:" $((++counter))
  спать 1
Выполнено

Команда trap содержит команду echo и команду exit . Он будет запущен, когда SIGINT поднят. Остальная часть скрипта представляет собой простой цикл. Если вы запустите сценарий и нажмете Ctrl+C, вы увидите сообщение из определения trap , и сценарий завершится.

 ./сигинт.ш 

Использование trap в скрипте для перехвата Ctrl+c

Мы можем использовать trap с сигналом ERR , чтобы перехватывать ошибки по мере их возникновения. Затем их можно передать команде или функции. Это «trap.sh». Мы отправляем уведомления об ошибках в функцию error_handler .

 #!/бин/баш

ловушка 'error_handler $? $LINENO' ОШИБКА

error_handler() {
  echo "Ошибка: ($1) произошла на $2"
}

главный() {
  echo "Внутри функции main()"
  плохая_команда
  второй
  в третьих
  выйти $?
}

второй() {
  echo "После вызова main()"
  echo "Внутри функции second()"
}

в третьих() {
  echo "Внутри третьей() функции"
}

главный

Основная часть скрипта находится внутри функции main , которая вызывает second и third функции. При обнаружении ошибки — в данном случае из-за того, что bad_command не существует — оператор trap направляет ошибку в функцию error_handler . Он передает статус выхода из неудачной команды и номер строки в функцию error_handler .

 ./ловушка.ш 

Использование trap с ERR для отлова ошибок в скрипте

Наша функция error_handler просто выводит сведения об ошибке в окно терминала. Если вы хотите, вы можете добавить в функцию команду exit , чтобы сценарий завершился. Или вы можете использовать серию операторов if/elif/fi для выполнения разных действий для разных ошибок.

Некоторые ошибки можно исправить, другие могут потребовать остановки скрипта.

Последний совет

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

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

 bash -x ваш-script.sh

Bash записывает вывод трассировки в окно терминала. Он показывает каждую команду с ее аргументами, если они есть. Это происходит после раскрытия команд, но до их выполнения.

Это может оказать огромную помощь в отслеживании неуловимых ошибок.

СВЯЗАННЫЕ С: Как проверить синтаксис сценария Linux Bash перед его запуском