С чего начать Первые шаги трек · 15 ступеней
Linux Labs

test, [ ] и [[ ]] — проверка условий

6 мин Ubuntu 24.04 · bash 5.2 · dash 0.5

test и [ ] проверяют файл, строку или число и отвечают кодом: 0 — да, 1 — нет; [[ ]] — их версия в bash.

Шпаргалка

8 команд Версия для печати
  • [ -f /etc/app.conf ] && echo "файл есть"
  • [ -d "$dir" ] || mkdir -p "$dir"
  • [ -z "$1" ] && { echo "Usage: $0 FILE" >&2; exit 2; }
  • [ "$answer" = yes ] строки равны
  • [ "$count" -gt 10 ] число больше 10
  • [ -r "$f" ] && [ -s "$f" ] читается и не пустой
  • [[ $name == *.log ]] шаблон, только bash
  • [[ $ver =~ ^[0-9]+\.[0-9]+$ ]] регулярное выражение, только bash

Обзор

test проверяет условие и ничего не печатает — отвечает кодом завершения: 0 — условие верно, 1 — нет, 2 — ошибка в самом выражении. [ — та же команда под другим именем, последним аргументом она ждёт ]. Поэтому пробелы вокруг скобок обязательны: каждая часть выражения — отдельный аргумент.

text
student@ubuntu:~$ type [ [[ test
[ is a shell builtin
[[ is a shell keyword
test is a shell builtin

[[ ]] — ключевое слово bash: выражение разбирает сама оболочка, до подстановки переменных. Поэтому внутри [[ ]] переменная без кавычек не распадается на слова, < и > не становятся перенаправлением, а == сравнивает с шаблоном. В sh (dash) [[ ]] нет: скрипт с #!/bin/sh обходится [ ].

[ ] и test [[ ]]
где работает любая POSIX-оболочка bash и zsh
$x без кавычек делится на слова — ошибки безопасно
шаблон *.log нет ==, !=
регулярное выражение нет =~
два условия [ a ] && [ b ] [[ a && b ]]

Проверки бывают трёх видов:

  • файлы: -e существует, -f обычный файл, -d каталог, -L символическая ссылка, -r, -w, -x — право текущего пользователя, -s файл не пустой, a -nt b — a новее b;
  • строки: = (в bash и ==), !=, -z — пустая, -n — не пустая;
  • числа: -eq, -ne, -lt, -le, -gt, -ge.

! перед проверкой обращает её: [ ! -f "$f" ] — файла нет.

< и > сравнивают строки посимвольно: [[ 10 > 9 ]] ложно, потому что «1» идёт раньше «9». Числа сравнивай через -gt, -lt или (( )).

Ключи

15

Проверки test — в таблице ниже. Полный список — help test, у [[ ]] — man bash, раздел CONDITIONAL EXPRESSIONS.

Ключ Что делает Пример
-e FILE Файл существует (любой тип); битая ссылка — нет. [ -e /etc/app.conf ]
-f FILE Обычный файл — не каталог и не устройство. [ -f "$1" ] || exit 1
-d DIR Каталог. [ -d /srv/backup ] || mkdir -p /srv/backup
-r / -w / -x FILE Текущему пользователю можно читать, писать, запускать. [ -w "$dir" ]
-z / -n STRING Строка пустая / не пустая; переменную — в кавычках. [ -z "$1" ]
= / != A B Строки равны / не равны; в bash можно и ==. [ "$answer" = yes ]
-eq / -ne A B Целые числа равны / не равны. [ $# -eq 1 ]
-lt / -le / -gt / -ge A B Меньше, не больше, больше, не меньше. [ "$used" -ge 90 ]
-s FILE Файл существует и не пустой. [ -s report.txt ]
-L FILE Символическая ссылка — даже если цели нет. [ -L /etc/localtime ]
! EXPR Обратить проверку. [ ! -f "$lock" ]
[[ == ]] PATTERN Строка подходит под шаблон с * и ?; шаблон без кавычек. [[ $file == *.log ]]
[[ =~ ]] REGEX Строка подходит под регулярное выражение; группы — в BASH_REMATCH. [[ $ver =~ ^[0-9]+\.[0-9]+$ ]]
-nt / -ot A B Файл A новее (старше) файла B по времени изменения. [ app.conf -nt app.conf.bak ]
-a / -o «И», «или» внутри [ ]; устарели — пиши [ a ] && [ b ]. [ -d /tmp ] && [ -w /tmp ]

Ничего не найдено. Попробуй запрос короче.

Рецепты

4

Проверить аргумент скрипта

bash
if [ $# -ne 1 ]; then
  echo "Usage: $0 FILE" >&2
  exit 2
fi
if [ ! -r "$1" ]; then
  echo "не читается: $1" >&2
  exit 1
fi

$# — число аргументов. Переменные в проверке — в двойных кавычках: имя файла с пробелом останется одним аргументом.

Сравнить число из вывода команды

bash
used=$(df --output=pcent / | tail -1 | tr -dc '0-9')
if [ "$used" -ge 90 ]; then
  echo "корень заполнен на $used%" >&2
fi

-ge ждёт голое число. tr -dc '0-9' убирает пробелы и знак %, иначе проверка падает с integer expression expected.

Шаблон и регулярное выражение

bash
[[ $file == *.log ]] && echo "журнал"
if [[ $day =~ ^([0-9]{4})-([0-9]{2}) ]]; then
  echo "год ${BASH_REMATCH[1]}, месяц ${BASH_REMATCH[2]}"
fi

Для day=2026-10-20 второе условие напечатает год 2026, месяц 10: скобки выражения попадают в массив BASH_REMATCH. Правую часть не бери в кавычки: [[ app.log == "*.log" ]] сравнивает буквально и даёт 1.

Несколько условий сразу

bash
[ -d "$dir" ] && [ -w "$dir" ]     # и
[ "$x" = a ] || [ "$x" = b ]       # или
[[ -d $dir && -w $dir ]]           # то же в bash

Неполадки

8

[: =: unary operator expected

Переменная пустая и стоит без кавычек: [ $x = foo ] превратилось в [ = foo ]. Пиши [ "$x" = foo ].

[: too many arguments

В значении пробел: при x="a b" проверка [ $x = foo ] получила a, b, =, foo. Возьми переменную в кавычки или перейди на [[ ]].

[: a: integer expression expected

-eq, -gt и соседям досталось не число: пустая строка, 85%, слово. Выведи значение (echo "[$used]") и очисть его до цифр.

[-f: command not found и missing `]'

Нет пробела после [ или перед ]. [-f оболочка ищет как имя команды, а [ -f file] не находит последний аргумент ].

[: a: unexpected operator

Так sh (dash) отвечает на == внутри [ ]. В POSIX строки сравнивает =. Либо пиши =, либо запускай скрипт в bash: #!/bin/bash в первой строке.

[ -n $x ] верно для пустой переменной

Без кавычек пустое значение исчезает, остаётся [ -n ] — проверка непустой строки -n, и она верна. С кавычками [ -n "$x" ] даёт 1, как и ждёшь.

value too great for base

[[ 08 -eq 8 ]] и (( 08 )) читают число с нулём впереди как восьмеричное, а 8 и 9 в нём нет. Так ломаются месяц и день из date +%m. Пиши 10#$m или сравнивай в [ ]: там 08 — обычное десятичное.

-e и -f смотрят на файл, куда ведёт ссылка. Если цели нет, проверка ложна. Саму ссылку проверяет -L.

Опасно

2

[ 10 > 9 ] создаёт файл

Внутри [ ] знак > — перенаправление. Оболочка создаёт в текущем каталоге пустой файл 9, а проверка сводится к [ 10 ] и верна всегда — даже [ 1 > 9 ]:

text
student@ubuntu:/tmp$ [ 1 > 9 ]; echo $?
0
student@ubuntu:/tmp$ ls -l 9
-rw-rw-r-- 1 student student 0 Oct 20 10:01 9

Числа сравнивай через -gt.

Проверка и действие — две команды

Между [ -f "$lock" ] и созданием файла другой экземпляр скрипта успеет сделать то же самое. Для защиты от двойного запуска нужна блокировка — flock.

См. также

  • exit-codes — коды 0, 1, 2 и операторы &&, ||.
  • bash-scripting — if, циклы и аргументы скрипта.
  • command-syntax — кавычки и деление строки на аргументы.
  • globbing — шаблоны * и ?, те же, что в [[ == ]].
  • regex — синтаксис выражений для =~.
  • permissions — что значат r, w, x для -r, -w, -x.
  • find — проверки по многим файлам сразу.
  • shellcheck — найдёт переменные без кавычек в [ ].
Загрузка…