Вторая часть статьи продолжает тему условных конструкций в bash, начатую в предыдущей части.
При составлении скриптов часто требуется выполнить разные фрагменты кода в зависимости от истинности или ложности того или иного условия. В языке bash для реализации таких сценариев предусмотрено несколько условных операторов. Сегодня поговорим об условных операторах if и case, а также логических операторах && и ||.
Оператор if
Один из основных условных операторов в языке bash, как и в большинстве языков программирования, — оператор if (иногда его называют if-then-else).
Общий вид конструкции:
С одним условием и одним предусмотренным вариантом действий (либо их отсутствием):
if условие a; then
блок кода 1
fiС одним условием и двумя вариантами действий:
if условие a; then
блок кода 1
else
блок кода 2
fiС двумя условиями и тремя вариантами действий:
if условие a; then
блок кода 1
elif условие b; then
блок кода 2
else
блок кода 3
fiПосле оператора if помещают тело проверяемого условия. Таким условием могут быть следующие операторы и команды:
| 1. Одинарные квадратные скобки [ ... ] | Одинарные скобки — способ записи команды test — позволяют сравнивать строки. |
|
| 2. Команда test |
| |
| 3. Двойные квадратные скобки [[ ... ]] | Позволяют проверять условия, поддерживают сравнение с шаблонами (== с подстановочными знаками) и регулярными выражениями (=~), а также (без экранирования) логические операторы && и || |
|
| 4. Арифметические вычисления(( ... )) | Используются для сравнения чисел (в частности, чисел с плавающей запятой) и обрабатывают выражение внутри двойных скобок как математическое выражение |
|
| 5. Сравнение строк в двойных скобках [[ ... ]] с регулярными выражениями | При помощи оператора =~ внутри двойных скобок [[ ... ]] строки сравнивают с регулярными выражениями. |
|
| 6. Подстановка команд | В качестве условия можно использовать любую команду или скрипт: они возвращают true, если код возврата равен 0, и false, если ненулевой. |
|
| 7. Отрицание ! | Символ ! используют для отрицания проверяемого условия: оно возвращает true, если код возврата ненулевой, и false, если он равен 0. |
|
Код возврата (exit status, exit code) завершённого дочернего процесса — целое число, которое оболочка сообщает родительскому процессу. Код 0 означает успешное завершение команды, другие коды (как правило, 1) — ошибку.
Скобки (одинарные или двойные — об этой конструкции рассказано в этом разделе) должны отделяться от тела условия пробелами.
Конструкция if-then-else означает, что если условие а истинно, следует выполнить блок кода 1.
Ниже приведены примеры скриптов с одним, двумя и тремя предусмотренными вариантами действий в зависимости от результата проверки условия.
if — then: в зависимости от результата проверки выполняется действие, либо скрипт завершается.Этот скрипт проверяет, запущена ли служба
sshd.if systemctl is-active --quiet sshd; then echo "Служба sshd запущена." fi
Если служба запущена, подстановка команды systemctl is-active --quiet sshd возвращает 0. В этом случае выполняется блок then: отображается сообщение "Служба sshd запущена."
Если служба не запущена, условие не выполняется, команда echo внутри then пропускается, и работа оператора if завершается.
if — then — else: в зависимости от результата проверки производится либо одно действие, либо другое.
if [ -f "/etc/passwd" ]; then echo "Файл /etc/passwd существует." else echo "Файл /etc/passwd не существует." fi
Условие [ -f "/etc/passwd" ] проверяет, существует ли /etc/passwd как обычный файл.
Если файл существует, код возврата команды test — 0 (успешное завершение). Выполняется блок then: отображается сообщение "Файл /etc/passwd существует."
Если файл не существует, команда test возвращает код 1 (ошибка). Выполняется блок else: отображается сообщение "Файл /etc/passwd не существует."
if — elif — else: в зависимости от результата проверки производится либо одно, либо второе, либо третье действие.Этот скрипт проверяет, является ли число в переменной num положительным, отрицательным или равно 0:
num=-5 if (( num > 0 )); then echo "Число положительное." elif (( num < 0 )); then echo "Число отрицательное." else echo "Число — 0." fi
После оператора else указывают финальный вариант хода скрипта — действия по умолчанию: они выполняются, если все вышеперечисленные условия оказались ложными.
Конец действия оператора if обозначается ключевым словом fi. Если не поставить его в конце, выполнение скрипта прервётся с сообщением об ошибке.
Отступы от начала строки внутри блоков кода с операторами if и else не обязательны для интерпретации кода оболочкой, но упрощают его чтение для человека.
Если имеется более двух вариантов хода скрипта в зависимости от значения единственной переменной, удобнее использовать оператор case — работа с ним описана в разделе Оператор case.
Оператор case
В bash-скриптах оператор case используется для выполнения различных блоков команд в зависимости от истинности того или иного условия. Этот способ позволяет обработать в условной конструкции несколько вариантов значения одной переменной более компактно, чем с оператором if.
Каждый вариант — строка после case … in — описывает одно условие и выполняет набор команд, если условие истинно (то есть код возврата — 0).
- Если действие должно выполняться при истинности одного условия, строку
case … inзавершают ограничителем — двумя точками с запятой без пробела между ними;; - Если действие должно выполняться в случае истинности нескольких условий, эти условия разделяют символами
;;&
После обнаружения истинности одного условия (или нескольких, объединённых символом ;;&) программа завершает работу оператора case, не переходя к чтению прочих условий.
Хорошая практика — указывать подстановочный знак * (звёздочка, asterisk) в качестве финального условия, определяющего действие по умолчанию: например, вывод сообщения. Финальное условие срабатывает во всех случаях, не описанных в предыдущих условиях, и если они оказались ложными, выполняется действие по умолчанию.
Синтаксис условия в общем виде:
case $variable in
вариант1)
действие1
;;
вариант2)
действие2
;;
*)
действие3
;;
esacСледующий скрипт принимает на вход вводимое с клавиатуры имя пользователя и совершает с его учётной записью одно из указанных действий: добавление, удаление, блокировку или снятие блокировки.
#!/bin/bash
read -p "Укажите имя пользователя: " username
read -p "Укажите действие {add|remove|lock|unlock}: " action
case $action in
add)
sudo useradd -m "$username" && echo "Пользователь $username добавлен."
;;
remove)
sudo userdel -r "$username" && echo "Пользователь $username удалён."
;;
lock)
sudo usermod -L "$username" && echo "Пользователь $username заблокирован."
;;
unlock)
sudo usermod -U "$username" && echo "Пользователь $username разблокирован."
;;
*)
echo "Выберите одно из этих действий: {add|remove|lock|unlock}"
;;
esac- После запуска скрипта в консоли отображается сообщение: Укажите имя пользователя:
- Пользователь вводит имя пользователя.
- Оператор
readсчитывает введённое пользователем значение и помещает его в переменнуюusername. - Отображается сообщение: Укажите действие
{add|remove|lock|unlock}: - Пользователь указывает одно из указанных действий.
- Оператор
readсчитывает введённое пользователем значение и помещает его в переменнуюaction. - Если указано действие
add, командаsudo useradd -m "$username"добавляет пользователя с указанным именем.- Если пользователь успешно создан, отображается сообщение Пользователь <имя пользователя> добавлен.
- Если такой пользователь уже существует, отображается сообщение об ошибке от утилиты useradd: user <имя пользователя> already exists.
- Если указаны действия
remove(удаление),lock(блокировка) илиunlock(снятие блокировки), действия выполняются по аналогичному алгоритму, только с командамиuserdelиusermod, соответственно: если команда выполнена успешно, отображается сообщение об этом, если нет — сообщение об ошибке от соответствующей утилиты. - Если введён набор символов, не входящий в указанные варианты, отображается сообщение: Выберите одно из этих действий: {add|remove|lock|unlock}
If или case?
Для некоторых скриптов можно выбрать один из двух условных операторов:
- конструкцию
ifс одним или несколькими операторамиelifлибо вложеннымиif-конструкциями - или оператор
caseдля описания тех же вариантов действий.
Если нужно сравнить значение одной переменной с несколькими эталонными значениями, рекомендуется выбирать оператор case: в этом случае код выглядит лаконичнее, его проще и быстрее читать.
Для работы с несколькими переменными удобнее использовать оператор if. Во всех случаях рекомендуется учитывать, насколько человеку будет удобно читать и поддерживать скрипт.
Логические операторы && и ||
Операторы && (логическое И, конъюнкция) и || (логическое ИЛИ, дизъюнкция) часто встречаются в скриптах и в цепочках команд, в том числе в конструкциях с оператором if. Они позволяют обусловить выполнение следующей команды результатом выполнения предыдущей.
Конструкция && (логическое И)
Синтаксис в общем виде:
команда1 && команда2Если первая команда выполняется с кодом возврата 0, то есть успешно — выполняется вторая команда. Если выполнение команды1 завершается с кодом 1 (с ошибкой), то оболочка не переходит к команде2 и отображается сообщение об ошибке: например, о том, что файла или каталога не существует.
Пример:
sudo apt update && sudo apt install ffmpegsudoпозволяет непривилегированному пользователю выполнить действия, требующие прав суперпользователя;apt updateобновляет список доступных пакетов программного обеспечения из официальных репозиториев;apt install ffmpegустанавливает (или обновляет) указанный пакет только в том случае, если удалось обновить список пакетов.
Общий вид синтаксиса при использовании оператора && в условных конструкциях с if:
С одной операцией конъюнкции условий и одним предусмотренным вариантом действий:
if условие а && условие b; then
блок кода 1
fiС одной операцией конъюнкции условий и двумя предусмотренными вариантами действий:
if условие а && условие b; then
блок кода 1
else
блок кода 2
fiС операцией конъюнкции условий, дополнительным условием и тремя предусмотренными вариантами действий:
if условие а && условие b; then
блок кода 1
elif условие x; then
блок кода 2
else
блок кода 3
fiСледующий скрипт запускает веб-сервер nginx, если он не запущен:
#!/bin/bash
process="nginx"
if ! pgrep -x "$process" >/dev/null && sudo systemctl start "$process"; then
echo "Теперь $process запущен."
else
echo "$process уже работает, либо его не удалось запустить."
fi- Переменной
processприсваивается имя нужного процесса — в данном случае веб-сервераnginx. - Если
pgrepне находит (отрицание обозначается символом!) среди запущенных процессовnginx(по ключу-хищется точное соответствие указанной строке; выводpgrepне отображается на экране, а подавляется путем перенаправления на нулевое устройство/dev/null), оболочка переходит к команде после оператора&&— от имени суперпользователя запускается процессnginx.
Нулевое устройство — это файл устройства, который уничтожает все записанные в него данные, но сообщает, что операция записи прошла успешно. В UNIX-системах оно обозначается как /dev/null.
- Отображается сообщение: Теперь
nginxзапущен. - Если команда между операторами
ifи&&выполнена, то есть процессnginxнайден в списке запущенных процессов, отображается сообщение:nginx уже работает, либо его не удалось запустить.
Конструкция || (логическое ИЛИ)
Синтаксис в общем виде:
команда1 || команда2В этой комбинации команда2 выполняется только в случае, если выполнение команды1 завершилось кодом 1 (ошибка). Если команда1 вернула код 0 (успешное завершение), то комбинация прервётся — будет выполнена только команда1.
Пример:
dpkg -l | grep -qw "ffmpeg" || sudo apt install ffmpegВывод утилиты dpkg -l (вывести список установленных программ) передаётся утилите grep.
grepс ключами-q(вывод grep не отображается на экране; поиск завершается после первого найденного соответствия) и-w(поиск точного соответствия указанной строке) ищет среди установленных имя пакетаffmpeg.- Если пакет установлен, вторая команда
sudo apt install ffmpegне запускается. Если он отсутствует в системе, запускается установкаffmpeg.
Общий вид синтаксиса при использовании оператора || в условных конструкциях с if:
if условие а || условие b; then
блок кода 1
elif условие x; then # Если требуется указать дополнительный
# вариант результата сравнения
блок кода 2
else
блок кода 3
fiСледующий скрипт также запускает веб-сервер nginx, если он не запущен, но проверяет наличие запущенного процесса по другой схеме:
#!/bin/bash
process="nginx"
if pgrep -x "$process" >/dev/null || sudo systemctl start "$process"; then
echo "$process уже работает или успешно запущен"
else
echo "Не удалось запустить $process"
fi- Переменной
processприсваивается имя нужного процесса — в данном случае веб-сервераnginx. - Только если команда между операторами
ifи||(pgrepнаходит среди запущенных процессов строку сnginx; вывод этой команды не отображается на экране, а подавляется путем перенаправления в/dev/null) не выполняется, оболочка переходит к команде после||— от имени суперпользователя запускается процессnginx. - Отображается сообщение:
nginxуже работает или успешно запущен. - Если команда между операторами
ifи||не выполнена, то есть процессnginxнайден в списке запущенных процессов, отображается сообщение:Не удалось запустить nginx.
Если условие заключается в одинарные квадратные скобки [ … ], вместо && используют оператор -a (AND), вместо || указывают оператор -o (OR).
Корректная запись операторов && и || в одинарных квадратных скобках:
[ a = a -a b = b ]
[ a = a -o b = b ]
Корректная запись операторов && и || в двойных квадратных скобках:
[[ a = a && b = b ]]
[[ a = a || b = b ]]
Законы де Моргана
Законы де Моргана позволяют упростить логические выражения, в которых есть операторы II и && с отрицаниями — например, при проверке строк или чисел на соответствие определённым условиям.
Законы формулируются так:
НЕ (A И B) = (НЕ A) ИЛИ (НЕ B)
НЕ (A ИЛИ B) = (НЕ A) И (НЕ B)
Вот как можно применить законы де Моргана при сравнении строк и чисел в bash.
Сравнение строк
Следующее условие проверяет, что значение переменной name — не angle и не line:
Исходное выражение: ! ( "$name" == "angle" || "$name" == "line" )
Результат преобразования де Моргана: "$name" != "angle" && "$name" != "line"
Сравнение чисел
Следующее условие проверяет, что число х не входит в диапазон от 10 до 20 включительно:
Исходное выражение: ! ( x >= 10 && x <= 20 )
Результат преобразования де Моргана: x < 10 || x > 20
Подведем итоги: из двух частей этой статьи мы узнали, что условные операторы if и case позволяют выбирать между вариантами дальнейшего хода скрипта, а логические конструкции && (логическое И) и || (логическое ИЛИ) позволяют связать выполнение следующей команды с результатом выполнения предыдущей.
Без условных конструкций не обойтись как при написании скриптов для базовой автоматизации, так и при создании сложных сценариев с вложенными условиями.
Автор статьи: Клара Суботэ