Условные операторы и коды выхода
Каждая команда завершается числовым кодом: 0 означает успех, ненулевое — ошибку. $? хранит последний код выхода. [[ ]] — современная конструкция проверки bash; && и || выстраивают цепочки команд по успеху или ошибке.
Каждая выполняемая команда сообщает оболочке об успехе или неудаче через число. Ноль означает успех; всё остальное — ошибку. Это не соглашение — это протокол, на котором строятся if, &&, || и set -e. Скрипты, игнорирующие коды выхода, молча делают не то, когда отсутствует зависимость, файл не найден или сетевой вызов провалился. Как только начинаешь воспринимать коды выхода как управляющий сигнал, перестаёшь писать скрипты, которые «работают на моей машине», и начинаешь писать скрипты, падающие громко и рано.
После этого урока ты сможешь читать $? для проверки последнего кода выхода, использовать [[ ]] для безопасных условных проверок, писать ветки if/elif/else, выстраивать цепочки команд через && и ||, и понимать, почему [ ] и [[ ]] — не одно и то же.
Каждая команда завершается с кодом в $?. 0 = успех. 1–255 = ошибка (конкретное значение зависит от программы). Читай его сразу после команды — следующая команда перезапишет его.
ls /etc/passwd
echo "код выхода: $?" # выводит: код выхода: 0
ls /nonexistent
echo "код выхода: $?" # выводит: код выхода: 2grep завершается с кодом 0, если найдено хотя бы одно совпадение, 1 — если совпадений нет, 2 — при ошибке. ssh завершается с 0, если соединение и удалённая команда оба успешны. Каждый хорошо написанный Unix-инструмент следует этому контракту.
[[ условие ]] — современная конструкция проверки bash. Она вычисляет условие и завершается с 0 (истина) или 1 (ложь). Всегда ставь пробелы внутри скобок.
file="/etc/passwd"
[[ -f "$file" ]] && echo "существует" # -f: обычный файл существует
[[ -d "/tmp" ]] && echo "это каталог" # -d: каталог существует
[[ -r "$file" ]] && echo "читаемый" # -r: читаем текущим пользователем
x=5
[[ $x -gt 3 ]] && echo "больше" # -gt: числовое больше
[[ "$x" == "5" ]] && echo "равно" # ==: строковое равенство (glob-шаблон справа)
[[ "$x" =~ ^[0-9]+$ ]] && echo "число" # =~: совпадение с regex (bash 3.2+)Часто используемые проверки файлов: -f (файл), -d (каталог), -e (существует), -r (читаем), -w (записываем), -x (исполняем), -s (не пустой), -L (символическая ссылка).
&& выполняет правую часть только если левая завершилась с кодом 0. || выполняет правую часть только если левая завершилась с ненулевым кодом. Это самые распространённые условные шаблоны в однострочниках и скриптах.
mkdir -p /tmp/work && echo "создано" # echo только если mkdir успешен
cd /nonexistent || { echo "ошибка" >&2; exit 1; } # выход при ошибке cd|| — это шаблон «или умри»: добавляй его после любой команды, которая должна успешно завершиться для продолжения скрипта. Конструкция { ...; } позволяет выполнять несколько команд при ошибке.
Можно объединять оба оператора:
cp "$src" "$dst" && echo "ок" || echo "ОШИБКА"Осторожно: если echo "ок" сам потерпит неудачу (маловероятно, но возможно), ветка || тоже сработает. Для надёжной обработки ошибок предпочитай if.
if проверяет код выхода, а не булево значение. Любая команда может быть условием.
if [[ -f "$config" ]]; then
source "$config"
elif [[ -f "${config}.default" ]]; then
source "${config}.default"
else
echo "Конфиг не найден" >&2
exit 1
fiif cmd; then ... fi выполняет cmd, и если его код выхода равен 0, запускает блок then. Условие — всегда команда: [[ ]] просто команда, которая завершается с 0 или 1. Можно использовать любую команду:
if grep -q "error" /var/log/app.log; then
echo "найдены ошибки в логе"
figrep -q подавляет вывод и завершается с 0, если шаблон найден — чистый способ проверить содержимое файла без захвата вывода.
[ ] (POSIX test) старше и менее безопасен чем [[ ]]. Ключевое различие: [ ] выполняет разбиение по словам для переменных без кавычек; пустая или многословная переменная вызывает синтаксическую ошибку или неверный результат.
x=""
[ $x = "yes" ] # НЕВЕРНО — раскрывается в: [ = "yes" ] — синтаксическая ошибка
[[ $x == "yes" ]] # безопасно — [[ ]] никогда не разбивает свои аргументы по словамВ [[ ]] правая часть == — это glob-шаблон, а не буквальная строка. Для буквального сравнения заключи правую часть в кавычки: [[ "$x" == "yes" ]]. Для regex используй =~ без кавычек вокруг шаблона.
Используй [[ ]] во всех bash-скриптах. Используй [ ] только при написании POSIX sh скриптов для оболочек без [[ ]].
Защита скрипта деплоя проверками кода выхода.
#!/usr/bin/env bash
TARGET="$1"
[[ -z "$TARGET" ]] && { echo "Использование: $0 <хост>" >&2; exit 1; }
if ! ping -c1 -W2 "$TARGET" &>/dev/null; then
echo "Хост $TARGET недоступен" >&2
exit 1
fi
if ! ssh "$TARGET" "test -d /var/app"; then
echo "Каталог приложения отсутствует на $TARGET" >&2
exit 2
fi
rsync -az ./dist/ "$TARGET":/var/app/ && echo "Деплой завершён."[[ -z "$TARGET" ]] проверяет пустую строку. ! ping ... инвертирует код выхода — блок выполняется, когда ping не отвечает. Каждый путь ошибки пишет в stderr (>&2) и выходит с уникальным кодом, чтобы вызывающий мог различить «недоступен» и «каталог отсутствует».
▸Почему это работает
На macOS с bash 3.2 [[ ]] и &&/|| работают идентично современному bash. Одна заметка о переносимости: =~ для совпадения с regex в [[ ]] доступен в bash 3.2, но движок regex на macOS — BSD ERE (POSIX extended), которому не хватает некоторых Perl-измов. Придерживайся базовых ERE-шаблонов (^, $, [0-9]+, (a|b)) — скрипты будут работать на Linux и macOS bash 3.2.
▸Частая ошибка
Классическая ловушка: [ $var == value ] с пустым $var становится [ == value ] — синтаксическая ошибка. Ещё одна: сравнение чисел строковыми операторами. [[ "10" > "9" ]] истинно в числовом смысле, но ложно как строковое сравнение (потому что "1" сортируется перед "9" лексикографически). Для числовых сравнений всегда используй -gt, -lt, -eq, -ne, -ge, -le внутри [[ ]], или арифметику (( )): (( x > 9 )).
Каков код выхода успешно завершившейся команды в bash?
Каждая команда производит код выхода в $?: 0 — успех, ненулевой — ошибка. [[ ]] — безопасная конструкция проверки bash: никогда не разбивает операнды по словам, поддерживает =~ для regex, корректно обрабатывает пустые переменные. [ ] — запасной вариант POSIX; избегай его в bash-скриптах. && короткозамыкает и выполняет правую команду только при успехе; || — только при ошибке — вместе они позволяют писать однострочные защитные конструкции. if cmd; then ... fi использует код выхода любой команды как условие. Всегда отправляй сообщения об ошибках в stderr через >&2 и завершай с ненулевым кодом, чтобы вызывающий мог обнаружить ошибку.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.