Как перехватывать ошибки в сценариях Bash в Linux
Опубликовано: 2022-08-27
По умолчанию сценарий Bash в Linux сообщает об ошибке, но продолжает работать. Мы покажем вам, как самостоятельно обрабатывать ошибки, чтобы вы могли решить, что нужно делать дальше.
Обработка ошибок в сценариях
Обработка ошибок является частью программирования. Даже если вы пишете безупречный код, вы все равно можете столкнуться с ошибками. Среда на вашем компьютере меняется со временем, когда вы устанавливаете и удаляете программное обеспечение, создаете каталоги и выполняете обновления и обновления.
Например, сценарий, который раньше запускался без проблем, может столкнуться с трудностями, если пути к каталогам изменились или права доступа к файлу изменились. По умолчанию оболочка Bash печатает сообщение об ошибке и продолжает выполнение сценария. Это опасный дефолт.
Если неудавшееся действие критично для какой-либо другой обработки или действия, которое происходит позже в вашем сценарии, это критическое действие не будет успешным. Насколько катастрофическим это окажется, зависит от того, что пытается сделать ваш скрипт.
Более надежная схема выявляла бы ошибки и позволяла бы сценарию работать, если бы ему нужно было завершить работу или попытаться исправить неисправное состояние. Например, если каталог или файл отсутствуют, может быть достаточно, чтобы сценарий воссоздал их.
Если сценарий столкнулся с проблемой, после которой он не может восстановиться, он может завершить работу. Если сценарий должен завершить работу, у него может быть возможность выполнить любую необходимую очистку, например удалить временные файлы или записать состояние ошибки и причину завершения работы в файл журнала.
Обнаружение статуса выхода
Команды и программы генерируют значение, которое отправляется в операционную систему при завершении работы. Это называется их статусом выхода. Он имеет нулевое значение, если ошибок не было, или некоторое ненулевое значение, если произошла ошибка.
Мы можем проверить статус выхода — также известный как код возврата — команд, которые использует сценарий, и определить, была ли команда успешной или нет.
В Bash ноль соответствует истине. Если ответ команды отличается от истины, мы знаем, что возникла проблема, и можем предпринять соответствующие действия.
Скопируйте этот сценарий в редактор и сохраните его в файле с именем «bad_command.sh».
#!/бин/баш если ( ! плохая_команда ); тогда echo "bad_command помечает ошибку." выход 1 фи
Вам нужно будет сделать скрипт исполняемым с помощью команды chmod
. Это шаг, необходимый для того, чтобы сделать любой сценарий исполняемым, поэтому, если вы хотите опробовать сценарии на своем компьютере, не забудьте сделать это для каждого из них. Подставьте имя соответствующего скрипта в каждом случае.
chmod +x bad_command.sh
Когда мы запускаем скрипт, мы видим ожидаемое сообщение об ошибке.
./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
обеспечивает обнаружение и перехват ошибок в функциях оболочки.
Чтобы также перехватывать неинициализированные переменные, добавьте параметр -u
(не установлен). Чтобы убедиться, что ошибки обнаруживаются в передаваемых последовательностях, добавьте параметр -o pipefail
. Без этого статус выхода переданной последовательности команд является статусом выхода последней команды в последовательности. Неудачная команда в середине переданной последовательности не будет обнаружена. Опция -o pipefail
должна быть в списке опций.
Последовательность, которую нужно добавить в начало вашего скрипта:
установить -Eeuo pipefail
Вот короткий скрипт под названием «unset-var.sh» с неустановленной переменной.
#!/бин/баш set -Eeou pipefail эхо "$ unset_variable" echo "Мы видим эту строку?"
Когда мы запускаем скрипт, unset_variable распознается как неинициализированная переменная, и скрипт завершается.
./unset-var.sh
Вторая команда 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
с сигналом 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
.
./ловушка.ш
Наша функция error_handler
просто выводит сведения об ошибке в окно терминала. Если вы хотите, вы можете добавить в функцию команду exit
, чтобы сценарий завершился. Или вы можете использовать серию операторов if/elif/fi
для выполнения разных действий для разных ошибок.
Некоторые ошибки можно исправить, другие могут потребовать остановки скрипта.
Последний совет
Отлов ошибок часто означает упреждение того, что может пойти не так, и добавление кода для обработки этих непредвиденных ситуаций, если они возникнут. Это в дополнение к проверке правильности потока выполнения и внутренней логики вашего скрипта.
Если вы используете эту команду для запуска скрипта, Bash покажет вам вывод трассировки по мере выполнения скрипта:
bash -x ваш-script.sh
Bash записывает вывод трассировки в окно терминала. Он показывает каждую команду с ее аргументами, если они есть. Это происходит после раскрытия команд, но до их выполнения.
Это может оказать огромную помощь в отслеживании неуловимых ошибок.
СВЯЗАННЫЕ С: Как проверить синтаксис сценария Linux Bash перед его запуском