Условные тесты в Bash: [ ] и [[ ]] — подробное руководство
Двойные скобки [[ ]] в Bash дают более удобный и безопасный синтаксис для условных проверок: поддерживают globbing, регулярные выражения и логические операторы без необходимости экранирования/кавычек. Одинарные скобки [ ] и команда test совместимы с POSIX и работают в других шеллах, но требуют осторожности с пробелами, кавычками и подстановкой имён файлов. Выбирайте [[ ]] для скриптов, явно запускаемых в Bash; используйте [ ] для максимальной портируемости.
Быстрые ссылки
- Single and Double Brackets
- Builtins and Keywords
- Shell Globbing
- Quoting Strings
- Filename Globbing Gotchas
- Logical AND and OR
- Regexes
- Just One Condition
Условные тесты управляют ветвлением выполнения Bash-скриптов в зависимости от логического выражения. Двойные скобки упрощают синтаксис, но имеют свои подводные камни.
Single and Double Brackets
Bash обеспечивает команду test. Она позволяет проверять логические выражения. Выражение возвращает код выхода: 0 означает true, любое другое значение — false.
Цепочка команд в командной строке с помощью оператора && использует это поведение: следующая команда выполнится только если предыдущая завершилась успешно.
Если проверка истинна, слово “Yes” будет напечатано.
test 15 -eq 15 && echo "Yes"test 14 -eq 15 && echo "Yes"Одинарные скобки [ ] имитируют поведение команды test. Они оборачивают выражение в квадратные скобки и функционируют так же, как test. По сути это одна и та же программа, созданная из одного исходника. Единственное различие — обработка запросов помощи.
Из исходников видно, что поведение обработки опций отличается для формы test и для формы [ при отсутствии завершающей “]”.
/* Recognize --help or --version, but only when invoked in theФорма “[“ при последнем аргументе, не равном “]”, обрабатывает –help/–version иначе, чем test.
Мы можем проверить это, запросив помощь у test и [ и посмотрев код возврата в Bash.
test --helpecho $?[ --helpecho $?
Обе команды — test и [ — присутствуют в Bash как встроенные (builtins). Но также доступна и внешняя бинарная версия [.
type testtype [whereis [![Вывод команд, показывающий типы команд test и .
В отличие от этого, двойные скобки [[ и ]] — это ключевые слова (keywords). Они также выполняют логические проверки, но синтаксис у них иной. Так как это ключевые слова, внутри них доступны функции, которые не работают в форме с [ ].
Двойные скобки поддерживаются Bash (и некоторыми совместимыми шеллами), но не во всех. Например, Korn shell (ksh) поддерживает их, а sh — нет. Во всех примерах в статье скрипты начинаются с:
#!/bin/bashЭто гарантирует, что сценарий будет выполняться именно в Bash.
Builtins and Keywords
Список встроенных команд (builtins) можно получить через compgen:
compgen -b | fmt -w 70(Без передачи через fmt строки будут в столбик; fmt группирует их в абзацы.)

Мы увидим test и [ в этом списке, но одиночная “]” не отдельно. Команда [ ищет завершающую “]” чтобы понять конец выражения, но “]” не является отдельной встроенной командой.
Ключевые слова показаны так:
compgen -k | fmt -w 70[[ и ]] — отдельные элементы в списке ключевых слов, поскольку [[ рассматривается как одно ключевое слово, а ]] — как другое. Они образуют парную конструкцию, как if/fi или case/esac.
Когда Bash парсит скрипт и встречает ключевое слово с парным закрывающим ключевым словом, он обрабатывает всё между ними особым образом.
С другой стороны, для builtin-команды все последующие аргументы передаются ей как параметры обычной команды, поэтому автор скрипта должен учитывать пробелы в значениях переменных и необходимость кавычек.
Shell Globbing
Двойные скобки поддерживают shell globbing. Это значит, что символ * будет расширяться как “любая последовательность символов”.
Скопируйте следующий текст в файл whelkie.sh:
#!/bin/bash
stringvar="Whelkie Brookes"
if [[ "$stringvar" == *elk* ]];
then
echo "Warning contains seafood"
else
echo "Free from molluscs"
fiСделайте скрипт исполняемым:
chmod +x whelkie.shПри запуске скрипта строка “elk” найдена в “Whelkie”, несмотря на окружающие символы:
./whelkie.shОбратите внимание: если вы возьмёте шаблон в двойные кавычки (например “elk“), то globbing не произойдёт — шаблон будет рассматриваться буквально.
Другие формы globbing тоже поддерживаются: ? соответствует одному символу, а диапазоны внутри [] — набору символов. Например, чтобы покрыть оба варианта регистра:
#!/bin/bash
stringvar="Jean-Claude van Clam"
if [[ "$stringvar" == *[cC]lam* ]];
then
echo "Warning contains seafood."
else
echo "Free from molluscs."
fiСохраните как damme.sh, сделайте исполняемым и запустите.
Quoting Strings
Ранее мы упомянули, что обёртывание шаблонов в двойные кавычки отключает globbing. Тем не менее, при использовании [[ ]] нет строгой необходимости всегда брать в кавычки переменные, даже если они содержат пробелы — [[ ]] защищает от разбивания по пробелам в большинстве случаев.
Пример:
#!/bin/bash
stringvar="van Damme"
surname="van Damme"
if [[ $stringvar == $surname ]];
then
echo "Surnames match."
else
echo "Surnames don't match."
fiСохраните как surname.sh, сделайте исполняемым и запустите.
Даже с пробелами сравнение сработает. Это удобно при работе с путями и именами каталогов с пробелами. В частности, опция -d возвращает true, если переменная содержит существующий каталог:
#!/bin/bash
dir="/home/dave/Documents/Needs Work"
if [[ -d ${dir} ]];
then
echo "Directory confirmed"
else
echo "Directory not found"
fiИзмените путь на свой, сохраните как dir.sh, сделайте исполняемым и выполните.
Filename Globbing Gotchas
Различие между [ ] и [[ ]] проявляется при проверках, где участвуют шаблоны имён файлов. Шаблон “*.sh” совпадает со всеми скриптами. При использовании одинарных скобок [ ] поведение может вызвать ошибку, если шаблон разворачивается в несколько файлов: тогда неподходящее количество аргументов ломает проверку.
Пример с [ ]:
#!/bin/bash
if [ -a *.sh ];
then
echo "Found a script file"
else
echo "Didn't find a script file"
fiСохраните как script.sh и запустите в каталоге с несколькими .sh-файлами — Bash выбросит ошибку.
Если оставить только один скрипт (только один файл, соответствующий *.sh), проверка отработает.
Если же переписать скрипт на [[ ]]:
#!/bin/bash
if [[ -a *.sh ]];
then
echo "Found a script file"
else
echo "Didn't find a script file"
fiи запустить в каталоге с множеством .sh-скриптов, ошибок не будет, но выражение, скорее всего, не найдёт файл, потому что внутри [[ ]] шаблон интерпретируется буквально, если он не разворачивается внешним шеллом.
Иначе говоря, [[ -a .sh ]] вернёт true лишь в том необычном случае, если в каталоге действительно есть файл с именем “.sh”.
Совет: для проверки наличия любого файла по маске используйте цикл или команду glob-обработки в оболочке, например:
shopt -s nullglob
files=( *.sh )
if (( ${#files[@]} )); then
echo "Found a script file"
else
echo "Didn't find a script file"
fiЭто надёжный и переносимый способ проверить наличие совпадающих файлов.
Logical AND and OR
Двойные скобки позволяют использовать && и || как логические операторы AND и OR внутри самого выражения.
Пример AND (оба условия должны быть истинны):
#!/bin/bash
first=10
second=25
if [[ $first -eq 10 && $second -lt 26 ]];
then
echo "Condition met"
else
echo "Condition failed"
fiСохраните как and.sh и запустите.
Пример OR (хотя бы одно условие истинно):
#!/bin/bash
first=10
second=25
if [[ $first -gt 15 || $second -lt 26 ]];
then
echo "Condition met."
else
echo "Condition failed."
fiСохраните как or.sh и запустите.
Важно: при использовании логических операторов внутри [[ ]] не забывайте про порядок операций и, при необходимости, группировку с помощью круглых скобок.
Regexes
[[ ]] поддерживают оператор =~, который применяет регулярное выражение (regex) к строковому выражению. Если regex удовлетворён — результат true.
Сохраните в regex.sh:
#!/bin/bash
words="one two three"
WordsandNumbers="one 1 two 2 three 3"
email="dave@fabricateddomain.co.uk"
mask1="[0-9]"
mask2="[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,4}"
if [[ $words =~ $mask1 ]];
then
echo "\"$words\" contains digits."
else
echo "No digits found in \"$words\"."
fi
if [[ $WordsandNumbers =~ $mask1 ]];
then
echo "\"$WordsandNumbers\" contains digits."
else
echo "No digits found in \"$WordsandNumbers\"."
fi
if [[ $email =~ $mask2 ]];
then
echo "\"$email\" is a valid e-mail address."
else
echo "Couldn't parse \"$email\"."
fiОписание шаблона mask2:
- [A-Za-z0-9.%+-]+ — одна или более букв, цифр или символов . % + -
- @ — символ @
- [A-Za-z0-9.-]+ — одна или более букв, цифр, точек или дефисов
- . — точка
- [A-Za-z]{2,4} — доменная часть из 2–4 букв
Этот regex достаточно прост и покрывает базовый формат e-mail, но не все возможные случаи (см. раздел “Когда это не работает”).
Запустив скрипт, вы увидите:
- Первая проверка вернёт false — в words нет цифр.
- Вторая — true — там есть цифры.
- Третья — true — адрес соответствует простому шаблону.
Just One Condition
Двойные скобки делают проверки гибкими и более читаемыми. Возможность использовать regex в проверках — веская причина освоить [[ ]] в Bash-скриптах.
Просто убедитесь, что скрипт запускается в шелле, поддерживающем [[ ]], то есть в Bash.
Важно: если скрипт должен быть портируемым на sh, используйте POSIX-совместимые конструкции и избегайте [[ ]] или выполняйте проверку шелла перед исполнением.
Когда это не работает — типичные ошибки и случаи
- Портируемость: /bin/sh может не поддерживать [[ ]]. Если ваш скрипт запускают в sh — поведение будет отличаться. Для совместимости используйте [ ] или test.
- Regex: оператор =~ ведёт себя по-разному для разных версий Bash в части обработки кавычек и слэшей. Никогда не берите regex в кавычки при использовании =~, иначе он будет восприниматься буквально.
- Globbing в [[ ]] может интерпретироваться иначе, чем вы ожидаете: шаблон внутри [[ ]] иногда не разворачивается так, как во внешнем контексте.
- Имена переменных: забытые $ перед переменной (напр. if [[ first -eq 10 ]] вместо [[ $first -eq 10 ]]) сравнивают литералы, а не значения.
- Специальные символы: при использовании расширенных регулярных выражений учитывайте, что некоторые символы нужно экранировать, и что поведение в Perl-совместимых regex отличается от POSIX ERE.
Альтернативные подходы и шаблоны проектирования
- Для портируемости: используйте test/[ ] и аккуратно экранируйте пробелы. Это правильно при написании скриптов, которые должны работать в /bin/sh.
- Для надёжной работы с группами файлов: включайте shopt -s nullglob и работайте с массивами для проверки наличия совпадающих файлов.
- Если нужно сложное регулярное сопоставление: используйте external tools (grep -E, awk, sed, perl) и проверяйте их код возврата.
- Для проверки пути используйте встроенные проверки -d, -f, -r и т. п. (они работают и в [ ] и в [[ ]]).
Ментальные модели и эвристики
- “[[ ]] — это безопасный и расширенный режим Bash для проверок; [ ] — это POSIX-совместимый минимум.”
- Используйте [[ ]] когда вы контролируете интерпретатор (#!/bin/bash).
- Используйте [ ] или test, если скрипт должен выполняться в самых разных шеллах.
- Для работы с файлами и шаблонами полагайтесь на возможность явной обработки glob-расширений (shopt/nullglob).
Быстрая таблица совместимости
| Конструкция | Поддерживается в Bash | Поддерживается в sh | Комментарий |
|---|---|---|---|
| test | да | да | POSIX-совместима |
| [ ] | да | да | требует закрывающую “]” |
| [[ ]] | да | нет | расширенная, Bash-only |
| =~ | да | нет | regex только в [[ ]] |
Чеклист для разработчика / системного администратора
- Скрипт начинается с #!/bin/bash, если используется []
- Нет незамеченных шаблонов (*.sh) в [] проверках
- Regex в =~ не заключён в кавычки
- При работе с файлами использован nullglob/массивы, если ожидается несколько совпадений
- Логические выражения явно сгруппированы (при необходимости)
- Есть тесты/примерные входные данные для всех ветвей if
Мини-методология: как выбрать между [ ] и [[ ]]
- Определите требуемую портируемость. Если нужен sh — выбирайте [ ] или test.
- Если нужен удобный синтаксис, globbing и regex — используйте [[ ]].
- При использовании [[ ]] убедитесь, что shebang указывает на /bin/bash.
- Добавьте тесты/юнит-скрипты для критичных ветвей и edge-case-ов.
Тесты и критерии приёмки
- Скрипт, использующий [[ ]] должен корректно выполняться при запуске через /bin/bash.
- Проверяя наличие файлов по шаблону, ожидаемый результат должен совпадать с поведением реального каталога в CI (учтите nullglob).
- Regex-проверки должны дать ожидаемые true/false на заранее подготовленных строках.
Примеры тест-кейсов
- Проверка: переменная = “a b”; условие [[ $var == “a b” ]] должно вернуть true.
- Проверка: каталог существует; if [[ -d $dir ]] должен вернуть true.
- Проверка: шаблон *.sh при нескольких файлах: поведение скрипта с [ ] — ошибка, с nullglob+массивом — успешная проверка наличия.
- Regex-проверка email: набор положительных и отрицательных примеров (валидные/невалидные адреса).
Runbook при проблемах
- Убедитесь, что shebang — #!/bin/bash.
- Запустите set -x для отладки: это покажет, как Bash расширяет переменные и шаблоны.
- Проверьте, не заключены ли шаблоны в кавычки (это отключит globbing).
- Для regex проверьте, не экранированы ли спецсимволы излишне.
- Если дело в файлах — включите shopt -s nullglob и вывод массива файлов для инспекции.
Безопасность и конфиденциальность
- Никогда не подставляйте пользовательский ввод напрямую в команды без валидации; в условных проверках это может привести к неожиданным результатам.
- Для парсинга и проверки внешних данных лучше применять строгие regex и дополнительные проверки, а не полагаться только на =~.
Альтернативы для локали и окружения
- В окружениях с ограниченным набором шеллов (например, BusyBox sh) всегда используйте POSIX-совместимые конструкции.
- Если нужно кросс-платформенное поведение (Linux и *BSD), тестируйте в CI на соответствующих образах.
Полезные сниппеты (cheat sheet)
- Проверить существование файла:
if [ -f "$file" ]; then echo "file"; fi- Проверить существование каталога (Bash):
if [[ -d $dir ]]; then echo "dir exists"; fi- Нахождение совпадающих файлов без ошибки:
shopt -s nullglob
files=( *.sh )
if (( ${#files[@]} > 0 )); then echo "Found"; fi- Regex-проверка без кавычек:
if [[ $s =~ [0-9]{3}-[0-9]{2}-[0-9]{4} ]]; then echo "matched"; fiDecision flow (Mermaid)
flowchart TD
A[Начало: нужно условие в скрипте?]
A --> B{Будет ли скрипт запускаться в /bin/bash?}
B -- Да --> C[Используйте [[ ]] для удобства и regex]
B -- Нет --> D[Используйте [ ] / test для портируемости]
C --> E{Нужна проверка файлов по шаблону?}
E -- Да --> F[Используйте nullglob + массивы или цикл]
E -- Нет --> G[Обычные проверки -d, -f и т.п.]
D --> G
F --> H[Тесты в CI: проверьте каталоги с 0,1,>1 файлами]
G --> H
H --> I[Готово]Короткое резюме
- [[ ]] — расширенная, удобная и безопасная конструкция для Bash; поддерживает regex, логические операторы и упрощает работу с пробелами.
- и test — POSIX-совместимые и подходят там, где важна переносимость.
- Для проверки масок файлов используйте nullglob и массивы, чтобы избежать ошибок при развертывании шаблонов.
Ключевые выводы
- Выбирайте конструкцию в зависимости от требуемой совместимости и возможностей окружения.
- Тестируйте сценарии с разными наборами данных: пустыми, однозначными и множественными совпадениями.
- Используйте set -x и shopt для локальной отладки и управления поведением расширений.
Спасибо за прочтение — используйте эти практики, чтобы скрипты были предсказуемыми и надёжными.


Похожие материалы
Как смотреть парад Macy's в День благодарения
Настройка графики NVIDIA: Control Panel и GeForce Experience
Как управлять LIFX с Windows
Добавить BlackArch в Arch Linux — быстрый гайд
Добавление маркеров в Microsoft Word