ServerAID
Найти гайд, команду, тег… ⌘ K
Shell и скрипты

bash if else elif fi: условия в скриптах с примерами

Условие в bash — это `if [[ … ]]; then … fi`. Разбираем разницу между `[[ ]]` и `[ ]`, сравнение строк и чисел, проверки файлов через `-f`/`-d`/`-x`, цепочки `elif`, связку через `&&`/`||` и боевые примеры из реальных скриптов: проверка root, проверка существования файла, проверка наличия команды.

bash if else: условные конструкции в shell-скриптах

Условие в bash — это if [[ … ]]; then … fi. Разбираем разницу между [[ ]] и [ ], сравнение строк и чисел, проверки файлов через -f/-d/-x, цепочки elif, связку через &&/|| и боевые примеры из реальных скриптов: проверка root, проверка существования файла, проверка наличия команды.

Минимальный синтаксис

В bash условие выглядит так:

if [[ "$user" = "root" ]]; then
    echo "Это root"
else
    echo "Это $user"
fi

Разберём по частям:

  • if — начало.
  • [[ "$user" = "root" ]] — само условие. Это команда, которая возвращает 0 (успех) или 1 (неуспех).
  • then — что выполнить, если условие истинно (код возврата 0).
  • else — необязательная ветка для ложного условия.
  • fi — конец блока. Это просто if наоборот.

then пишут на одной строке с if через ; или с новой строки. Точка с запятой нужна потому, что then сама по себе — отдельная команда.

В скриптах строка с шебангом обязательна, иначе скрипт может выполниться sh, у которого нет [[ ]]:

#!/bin/bash

if [[ -f /etc/os-release ]]; then
    echo "Ubuntu/Debian-подобная система"
fi

#!/bin/bash — не комментарий, а инструкция ядру: «запускать через bash». Без неё ./script.sh пойдёт через /bin/sh (это dash на Ubuntu), и многие конструкции сломаются.

Различаем [[ ]] и [ ]

Это самая частая путаница. Обе формы — синтаксис проверки, но они разные:

  • [ … ] — это команда test, унаследованная из POSIX-shell. Совместима с sh/dash. Чувствительна к пробелам, требует кавычек вокруг переменных.
  • [[ … ]] — встроенная конструкция bash (и zsh, ksh). Безопаснее: правильно работает с пробелами в переменных, поддерживает регулярки, логические операторы &&/|| внутри.

В современных скриптах для Ubuntu и macOS используйте [[ ]]. Используйте [ ] только если скрипт действительно должен работать в dash или старом sh (например, init-скрипты Alpine Linux).

Что ломается с [ ] без кавычек:

# Если $name пустое или содержит пробелы — синтаксическая ошибка
if [ $name = "Вася" ]; then ...

# Безопасный POSIX-вариант — всегда кавычки
if [ "$name" = "Вася" ]; then ...

# В bash просто пишем [[ ]] и не думаем о кавычках
if [[ $name = "Вася" ]]; then ...

Дальше во всех примерах — [[ ]]. Если вы пишете под #!/bin/sh — мысленно меняйте на [ ] и оборачивайте переменные в кавычки.

Сравниваем строки и числа

В bash для сравнений строк и чисел разные операторы, и это вторая частая путаница:

Действие Строки Числа
Равно = или == -eq
Не равно != -ne
Меньше < (только в [[ ]]) -lt
Меньше или равно -le
Больше > (только в [[ ]]) -gt
Больше или равно -ge
Пусто -z "$var"
Не пусто -n "$var"
# Сравнение строк
if [[ "$env" == "production" ]]; then
    echo "Прод"
fi

# Сравнение чисел
count=$(ls /var/log | wc -l)
if [[ $count -gt 100 ]]; then
    echo "Слишком много файлов в /var/log"
fi

# Проверка пустой строки (например, обязательный аргумент)
if [[ -z "$1" ]]; then
    echo "Использование: $0 <имя>"
    exit 1
fi

Что произойдёт, если перепутать: [[ "5" -gt "10" ]] вернёт false (потому что 5 не больше 10), а [[ "5" > "10" ]] вернёт true (потому что строка "5" лексикографически больше "10"). Это ловится только в продакшне на больших числах.

Проверяем файлы и каталоги

Самая полезная семья проверок — флаги -f, -d, -x и компания:

Флаг Что проверяет
-e path Существует (файл, каталог, симлинк, что угодно)
-f path Обычный файл (не каталог, не устройство)
-d path Каталог
-L path Символическая ссылка
-r path Доступен для чтения текущему процессу
-w path Доступен для записи
-x path Исполняемый
-s path Существует и непустой
path1 -nt path2 path1 новее path2 (newer than)
path1 -ot path2 path1 старее path2 (older than)
CONFIG=/etc/myapp/config.yaml

if [[ ! -f "$CONFIG" ]]; then
    echo "Не найден конфиг: $CONFIG" >&2
    exit 1
fi

if [[ ! -r "$CONFIG" ]]; then
    echo "Нет прав на чтение $CONFIG" >&2
    exit 1
fi

Восклицательный знак ! инвертирует результат: «если файл НЕ существует». Можно ставить и так: if [[ -f "$CONFIG" ]]; then ... else ... fi — выбирайте по читаемости.

Используем elif для нескольких веток

Когда условий три и более, не вкладывайте if в else — используйте elif:

release=$(lsb_release -rs)

if [[ "$release" == "24.04" ]]; then
    echo "Ubuntu 24.04 LTS — поддерживается до 2034"
elif [[ "$release" == "22.04" ]]; then
    echo "Ubuntu 22.04 LTS — поддерживается до 2032"
elif [[ "$release" == "20.04" ]]; then
    echo "Ubuntu 20.04 LTS — поддерживается до 2030"
else
    echo "Неизвестный или неподдерживаемый релиз: $release" >&2
    exit 1
fi

elif — это «иначе, если». Их можно цепочкой сколько угодно. Если веток становится больше пяти, посмотрите на case — он в bash для подобных случаев читается лучше:

case "$release" in
    24.04) echo "Ubuntu 24.04 LTS" ;;
    22.04) echo "Ubuntu 22.04 LTS" ;;
    20.04) echo "Ubuntu 20.04 LTS" ;;
    *)     echo "Неизвестный релиз" >&2; exit 1 ;;
esac

Связываем условия через && и ||

Внутри [[ ]] используйте && (И) и || (ИЛИ):

if [[ -f /etc/myapp/config.yaml && -r /etc/myapp/config.yaml ]]; then
    echo "Конфиг есть и читается"
fi

if [[ "$user" == "root" || "$user" == "deploy" ]]; then
    echo "Привилегированный пользователь"
fi

В POSIX [ ] для этого использовали -a (и) и -o (или), но они нестрого определены и могут давать неожиданные результаты. В [[ ]] всегда &&/||.

Между командами &&/|| работают как «выполнить если предыдущая успешна / неуспешна»:

mkdir -p /var/log/myapp && chown myapp:myapp /var/log/myapp

Если mkdir упадёт, chown не выполнится. Это компактная альтернатива if:

# Длинно через if
if mkdir -p /var/log/myapp; then
    chown myapp:myapp /var/log/myapp
fi

# Коротко через &&
mkdir -p /var/log/myapp && chown myapp:myapp /var/log/myapp

Используйте && для коротких цепочек из 2–3 команд. Если логика разрастается — переходите на if, читаемость важнее лаконичности.

Проверяем код возврата прошлой команды

Любая команда возвращает код выхода: 0 — успех, не 0 — провал. Этот код доступен в $?:

git pull
if [[ $? -ne 0 ]]; then
    echo "git pull упал" >&2
    exit 1
fi

Но прямо в условии можно использовать саму команду — без $? это короче и надёжнее (некоторые команды между git pull и проверкой могут перезаписать $?):

if ! git pull; then
    echo "git pull упал" >&2
    exit 1
fi

! перед командой инвертирует код возврата — if ! срабатывает при неуспехе.

Боевые примеры

1. Проверка, что скрипт запущен от root.

if [[ $EUID -ne 0 ]]; then
    echo "Этот скрипт нужно запускать через sudo" >&2
    exit 1
fi

$EUID — эффективный UID процесса. У root он 0. Это надёжнее, чем сравнивать $USER == "root", потому что sudo не всегда меняет $USER.

2. Проверка, что команда установлена.

if ! command -v jq >/dev/null 2>&1; then
    echo "Нужен jq. Установите: sudo apt install -y jq" >&2
    exit 1
fi

command -v <name> возвращает 0, если команда найдена в PATH. Альтернатива — which jq, но command -v встроенный в bash и работает быстрее, без зависимости от пакета which.

3. Проверка, что скрипт получил аргументы.

if [[ $# -lt 1 ]]; then
    echo "Использование: $0 <slug>" >&2
    exit 1
fi

slug="$1"

$# — количество позиционных аргументов. $0 — имя скрипта.

4. Чтение значения по умолчанию из переменной окружения.

PORT="${PORT:-8080}"

if [[ ! "$PORT" =~ ^[0-9]+$ ]]; then
    echo "PORT должен быть числом, получено: $PORT" >&2
    exit 1
fi

${PORT:-8080} — взять $PORT, или 8080 если она пустая или не задана. =~ — regex-сравнение, доступно только в [[ ]].

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

for i in {1..30}; do
    if curl -fsS http://127.0.0.1:8080/health >/dev/null 2>&1; then
        echo "Сервис поднялся за ${i} секунд"
        exit 0
    fi
    sleep 1
done

echo "Сервис не поднялся за 30 секунд" >&2
exit 1

Используется в healthcheck-скриптах CI и деплоя.

Частые вопросы

Какой синтаксис у if в bash

Базовая форма: if [[ условие ]]; then команды; fi. С else: if [[ … ]]; then …; else …; fi. С несколькими ветками: if [[ … ]]; then …; elif [[ … ]]; then …; else …; fi. fi — обязательное завершение блока, это if написанное наоборот.

Чем отличается [[ ]] от [ ] в bash

[[ ]] — встроенная конструкция bash, безопаснее с пробелами в переменных, поддерживает regex (=~) и логические &&/|| внутри. [ ] — старая POSIX-команда test, требует кавычек вокруг переменных и работает в любом sh-совместимом shell. В скриптах под bash используйте [[ ]], под #!/bin/sh[ ] с кавычками.

Почему мой bash if не работает с пробелами

Скорее всего вы используете [ ] без кавычек. [ $name = "Вася" ], где $name пустое или содержит пробел, превращается в синтаксически некорректный [ = "Вася" ]. Решения два: либо всегда оборачивать переменную в кавычки [ "$name" = "Вася" ], либо использовать [[ $name = "Вася" ]] — там кавычки не обязательны.

Как сравнить два числа в bash

Для чисел используйте флаги -eq (равно), -ne (не равно), -lt (меньше), -le (меньше или равно), -gt (больше), -ge (больше или равно): if [[ $count -gt 100 ]]. Не путайте с операторами < и > — в [[ ]] они сравнивают строки лексикографически, не числа.

Как проверить, что файл существует

if [[ -f /path/to/file ]]; then ... — проверка обычного файла. Для каталога — -d, для любого пути (включая симлинки) — -e. Чтобы проверить «не существует», используйте [[ ! -f /path ]] или вынесите else: if [[ -f path ]]; then ... else ... fi.

Что значит [[ -z "$var" ]]

-z — true, если строка пустая (zero length). -n — true, если непустая. Часто используется для проверки обязательных аргументов: if [[ -z "$1" ]]; then echo "нужен аргумент"; exit 1; fi.

Почему скрипт работает в bash и не работает с #!/bin/sh

На Ubuntu /bin/sh — это dash, упрощённый POSIX-shell без [[ ]], без массивов, без ==, без =~. Если скрипт начинается с #!/bin/sh — пишите по POSIX ([ ] с кавычками, = для строк, -eq для чисел). Если используете bash-фичи — обязательно #!/bin/bash.

Что запомнить

  • В скриптах под bash используйте [[ ]], а не [ ]. Меньше шансов споткнуться о пробелы и пустые переменные.
  • Для строк — ==/!=, для чисел — -eq/-ne/-lt/-gt. Перепутать = получить тихие баги на больших числах.
  • Не забывайте #!/bin/bash в начале скрипта. Без него Ubuntu запустит ваш файл через dash, и многое сломается.
  • Проверка существования файла — -f (обычный файл), -d (каталог), -e (что угодно), -r/-w/-x (права).
  • Между командами &&/|| короче, чем if, но только для 2–3 команд. Дальше — if.
  • Для проверки кода возврата используйте if ! command; then ..., а не command; if [[ $? -ne 0 ]]; then — лаконичнее и надёжнее.
  • command -v <name> — самый простой способ проверить, что бинарь установлен.

Похожие материалы

grep — что это и зачем
Глоссарий

grep — что это и зачем

grep — стандартная команда Linux для поиска строк по шаблону внутри текстовых файлов и потоков. Берёт текст на вход, отдаёт строки, в которых нашлось совпадение. Базовый инструмент работы с логами, конфигами, выводом других команд через пайп.

Редакция
find — что это и зачем
Глоссарий

find — что это и зачем

find — стандартная команда Linux для поиска файлов и каталогов по имени, типу, правам, размеру, дате и десяткам других критериев. С помощью `-exec` поверх найденного можно сразу выполнять команды. Универсальный инструмент админа для массовой работы с файловой системой.

Редакция
LVM — что это и зачем
Глоссарий

LVM — что это и зачем

LVM — слой абстракции между физическими дисками и файловыми системами в Linux. Объединяет диски в пулы (Volume Groups), нарезает их на логические тома (Logical Volumes) и позволяет изменять размер на лету. Альтернатива классическому partitioning, которая делает работу с дисками гибкой.

Редакция
swap — что это и зачем
Глоссарий

swap — что это и зачем

swap — пространство на диске, которое Linux использует как «продолжение» оперативной памяти, когда физической RAM не хватает. Бывает в виде отдельного swap-раздела или файла. На современных серверах с большим количеством RAM swap всё равно нужен — но не для скорости, а для устойчивости.

Редакция