Как использовать set и pipefail в сценариях Bash в Linux

Терминал Linux на экране ноутбука на синем фоне.Фатмавати Ачмад Заэнури/Shutterstock.com

Команды Linux set и pipefail определяют, что произойдет, если в сценарии Bash произойдет сбой. Есть о чем подумать, чем остановиться или продолжаться.

Сценарии Bash и условия ошибок

Сценарии оболочки Bash великолепны. Они быстро пишутся и не требуют компиляции. Любое повторяющееся или многоэтапное действие, которое вам нужно выполнить, можно завернуть в удобный сценарий. А поскольку сценарии могут вызывать любые стандартные утилиты Linux, вы не ограничены возможностями самого языка оболочки.

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

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

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

Вот как их использовать.

Демонстрация проблемы

Вот тривиальный скрипт Bash. Он выводит на терминал две строки текста. Вы можете запустить этот скрипт, если скопируете текст в редактор и сохраните его как «script-1.sh».

#!/bin/bash echo Это произойдет первым echo Это произойдет вторым

Чтобы сделать его исполняемым, вам нужно использовать chmod:

chmod +x скрипт-1.sh

Вам нужно будет запустить эту команду для каждого скрипта, если вы хотите запустить их на своем компьютере. Запустим скрипт:

./скрипт-1.ш

Запуск простого скрипта без ошибок.

Две строки текста отправляются в окно терминала, как и ожидалось.

Немного изменим скрипт. Мы попросим ls указать сведения о несуществующем файле. Это не удастся. Мы сохранили это как «script-2.sh» и сделали его исполняемым.

#!/bin/bash echo Это произойдет первым ls imaginary-filename echo Это произойдет вторым

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

./скрипт-2.ш

Запуск сценария и создание условия отказа.

Несмотря на сбой команды ls, сценарий продолжал выполняться. И хотя при выполнении скрипта произошла ошибка, код возврата из скрипта в оболочку равен нулю, что говорит об успехе. Мы можем проверить это, используя echo и $? переменная, которая содержит последний код возврата, отправленный в оболочку.

эхо $?

Проверка кода возврата для последнего выполненного скрипта.

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

Опция set -e

Параметр set -e (выход) вызывает завершение сценария, если какой-либо из процессов, которые он вызывает, генерирует ненулевой код возврата. Все, что не равно нулю, считается ошибкой.

Добавив опцию set -e в начало скрипта, мы можем изменить его поведение. Это «script-3.sh».

#!/bin/bash set -e echo Это произойдет первым ls imaginary-filename echo Это произойдет вторым

Если мы запустим этот скрипт, мы увидим эффект set -e.

./script-3.sh эхо $?

Завершение скрипта при возникновении ошибки и правильная установка кода возврата.

Сценарий останавливается, и код возврата, отправляемый в оболочку, имеет ненулевое значение.

Устранение неполадок в трубах

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

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

истинное эхо $? ложное эхо $?

Встроенные команды оболочки bash true и false.

Если мы направим false в true — где false представляет неудачный процесс — мы получим код возврата true, равный нулю.

ложный | истинное эхо $?

Преобразование лжи в истину.

В Bash есть переменная массива с именем PIPESTATUS, которая фиксирует все коды возврата каждой программы в цепочке конвейеров.

ложный | правда | ложный | истинное эхо «${PIPESTATUS[0]} ${PIPESTATUS[1]} ${PIPESTATUS[2]} ${PIPESTATUS[3]}»

Использование PIPESTATUS для просмотра кода возврата всех программ в цепочке каналов.

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

Вот тут-то и появляются set -o (параметры) и pipefail. Это «script-4.sh». Это попытается передать содержимое несуществующего файла в wc.

#!/bin/bash set -e echo Это произойдет сначала cat script-99.sh | wc -l echo Это произойдет через секунду

Это терпит неудачу, как мы и ожидали.

./script-4.sh эхо $?

Запуск скрипта с ошибкой в ​​цепочке каналов.

Первый ноль — это вывод wc, говорящий нам, что он не прочитал ни одной строки для отсутствующего файла. Второй ноль — это код возврата из второй эхо-команды.

Мы добавим -o pipefail , сохраним его как «script-5.sh» и сделаем исполняемым.

#!/bin/bash set -eo pipefail echo Это произойдет сначала cat script-99.sh | wc -l echo Это произойдет через секунду

Давайте запустим это и проверим код возврата.

./script-5.sh эхо $?

Запуск сценария, который перехватывает ошибки в цепочках каналов и правильно устанавливает код возврата.

Сценарий останавливается, а вторая команда echo не выполняется. Код возврата, отправленный в оболочку, равен единице, правильно указывающей на сбой.

Перехват неинициализированных переменных

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

Это скрипт-6.sh.

#!/bin/bash set -eo pipefail echo «$notset» echo «Другая эхо-команда»

Мы запустим его и понаблюдаем за его поведением.

./script-6.sh эхо $?

Запуск скрипта, который не захватывает неинициализированные переменные.

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

Мы можем перехватить этот тип ошибки, используя опцию set -u (unset). Мы добавим это в нашу растущую коллекцию установленных параметров в верхней части скрипта, сохраним его как «script-7.sh» и сделаем его исполняемым.

#!/bin/bash set -eou pipefail echo «$notset» echo «Другая команда echo»

Запустим скрипт:

./script-7.sh эхо $?

Запуск скрипта, который захватывает неинициализированные переменные.

Обнаружена неинициализированная переменная, сценарий останавливается, а код возврата устанавливается равным единице.

Опция -u (unset) достаточно интеллектуальна не срабатывать ситуациями, когда вы можете законно взаимодействовать с неинициализированной переменной.

В «script-8.sh» скрипт проверяет, инициализирована ли переменная New_Var или нет. Вы не хотите, чтобы сценарий останавливался на этом, в реальном сценарии вы будете выполнять дальнейшую обработку и разбираться с ситуацией самостоятельно.

Обратите внимание, что мы добавили параметр -u в качестве второй опция в операторе set. Опция -o pipefail должна быть последней.

#!/bin/bash set -euo pipefail, если [ -z «${New_Var:-}» ]; затем эхо «New_Var не имеет назначенного значения». фи

В «script-9.sh» проверяется неинициализированная переменная, и если она неинициализирована, вместо нее предоставляется значение по умолчанию.

#!/bin/bash set -euo pipefail default_value=484 Value=${New_Var:-$default_value} echo «New_Var=$Value»

Сценарии разрешено выполнять до их завершения.

./скрипт-8.ш ./скрипт-9.ш

Запуск двух сценариев, в которых неинициализированные переменные обрабатываются внутри, а параметр -u не срабатывает.

Запечатанный топором

Еще одна удобная опция — set -x (выполнить и распечатать). Когда вы пишете сценарии, это может быть спасением. он печатает команды и их параметры по мере их выполнения.

Это дает вам быструю «грубую и готовую» форму трассировки выполнения. Выделение логических ошибок и обнаружение ошибок становится намного проще.

Мы добавим параметр set -x в «script-8.sh», сохраним его как «script-10.sh» и сделаем исполняемым.

#!/bin/bash set -euxo pipefail, если [ -z «${New_Var:-}» ]; затем эхо «New_Var не имеет назначенного значения». фи

Запустите его, чтобы увидеть линии трассировки.

./скрипт-10.ш

Запуск сценария со строками трассировки -x, записанными на терминал.

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

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован.