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>— самый простой способ проверить, что бинарь установлен.