Создать тридцать пронумерованных файлов вручную - занятие тоскливое. Набрать touch, перечислить имена, не сбиться в нумерации, не пропустить ни одного. А ведь оболочка умеет порождать такие списки сама, причём заметно быстрее цикла. Механизм называется brace expansion, развёртывание фигурных скобок, и он превращает компактный шаблон в длинную последовательность строк ещё до того, как команда начнёт работать.

Суть в том, что bash разворачивает фигурные скобки на самом раннем этапе разбора строки, раньше подстановки переменных и раскрытия шаблонов имён файлов. Из одного шаблона рождается набор готовых аргументов, и команда получает их так, будто всё перечислено руками. Это и роднит механизм со скоростью, и закладывает главную ловушку, о которой речь пойдёт ниже.

Списки и диапазоны рождают последовательности из компактного шаблона

У развёртывания две основные формы. Первая - список через запятую внутри скобок. Каждый элемент списка подставляется по очереди, а текст вокруг скобок повторяется для каждого.

echo файл_{один,два,три}.txt
# файл_один.txt файл_два.txt файл_три.txt

Вторая форма - диапазон, или последовательность. Здесь указывается начало и конец через две точки, и оболочка перечисляет всё, что между ними, включая границы. Диапазоны бывают числовыми и символьными.

echo {5..12}
# 5 6 7 8 9 10 11 12

echo {c..k}
# c d e f g h i j k

Числовые диапазоны не обязаны идти по возрастанию - если начало больше конца, последовательность пойдёт вниз. Это даёт обратный отсчёт без всяких ухищрений. Отрицательные числа тоже работают.

echo {5..1}
# 5 4 3 2 1

echo {3..-4}
# 3 2 1 0 -1 -2 -3 -4

Смешивать типы нельзя. Если по одну сторону точек стоит число, а по другую буква, оболочка не понимает такой диапазон и оставляет шаблон нетронутым - возвращает его буквально, как обычный текст. Это общее правило: любое некорректно составленное развёртывание bash не трогает вовсе.

echo {5..k}
# {5..k} - смесь числа и буквы не разворачивается

Шаг и нулевая набивка точно подгоняют последовательность под формат

Между началом и концом диапазона можно вставить ещё одну пару точек и указать шаг. Тогда последовательность пойдёт не подряд, а через заданный интервал. Это закрывает потребность в чётных, нечётных и любых равноотстоящих числах.

echo {0..15..2}
# 0 2 4 6 8 10 12 14 - чётные

echo {1..15..2}
# 1 3 5 7 9 11 13 15 - нечётные

echo {100..1000..99}
# 100 199 298 397 496 595 694 793 892 991

Отдельного внимания заслуживает нулевая набивка. Если хотя бы одно из чисел диапазона начинается с нуля, вся последовательность получает одинаковую ширину с ведущими нулями. Это бесценно для имён файлов, которые должны сортироваться по порядку, а не лексикографически - где file_10 оказывается раньше file_2, если нули не проставлены.

echo {01..10}
# 01 02 03 04 05 06 07 08 09 10

Когда у начала и конца разная ширина набивки, берётся наибольшая. Эта деталь сохраняет выравнивание даже там, где границы заданы небрежно. Важно лишь помнить про пробелы: между точками их быть не должно, иначе развёртывание молча провалится и шаблон останется текстом.

Приставка, окончание и вложенность собирают сложные имена

Развёртывание становится по-настоящему мощным, когда вокруг скобок добавляют постоянный текст. Текст перед скобками - приставка, текст после - окончание. Оба повторяются для каждого элемента.

echo 1.{0..9}
# 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9

echo access_log_{01..12}.log
# access_log_01.log ... access_log_12.log - месяц логов сразу

Когда одно развёртывание ставят приставкой или окончанием к другому, скобки перемножаются. Каждый элемент первого набора комбинируется с каждым элементом второго - получается то, что в математике зовут декартовым произведением. Это позволяет одной строкой породить целую сетку имён.

echo {A..C}{1..3}
# A1 A2 A3 B1 B2 B3 C1 C2 C3

Скобки можно и вкладывать одну в другую для более прихотливых узоров. Результаты при этом не сортируются - порядок строго слева направо, в том виде, в каком их перечислил шаблон.

echo {2024,2025}-{Q1,Q2,Q3,Q4}
# 2024-Q1 2024-Q2 ... 2025-Q4 - все кварталы двух лет

Массовое создание файлов и каталогов одной командой

Здесь развёртывание раскрывает свою практическую ценность в полную силу. Создание десятков файлов или каталогов, которое заняло бы цикл или ручное перечисление, ужимается в одну строку.

touch файл_{1..10}.txt
# создаст файл_1.txt ... файл_10.txt разом

mkdir проект{1..5}
# создаст пять каталогов за один вызов mkdir

Тот же приём ловко делает резервные копии и массовые переименования. Скобки с двумя вариантами окончания создают пару имён из одного.

cp config.yml{,.bak}
# превращается в: cp config.yml config.yml.bak

Этот сжатый трюк заслуживает разбора. Скобки {,.bak} дают два элемента: пустую строку и .bak. С приставкой config.yml они разворачиваются в два аргумента - оригинальное имя и имя с добавленным .bak. Получается копия в один присест, без повторного набора длинного имени.

Развёртывание удобно и за пределами создания файлов - например, чтобы передать команде несколько похожих аргументов без повторов.

grep -e {foo,bar,baz} файл.log
# ищет три образца за один вызов grep

Чистое развёртывание против внешней утилиты seq

У brace expansion есть давний соперник - внешняя утилита seq, которая тоже печатает числовые последовательности. Различий между ними два, и оба практически значимы.

Первое - скорость и природа. Развёртывание скобок встроено в оболочку и не порождает отдельного процесса. По некоторым замерам генерация последовательности скобками обгоняет цикл во много раз именно потому, что всё происходит внутри оболочки на этапе разбора. Утилита seq - внешняя программа, её запуск стоит дороже, хотя на единичном вызове разница незаметна.

Второе различие важнее и подводит к главной ловушке. Развёртывание скобок не понимает переменных. Совсем.

начало=1
конец=10
echo {$начало..$конец}
# {1..10}? Нет! Выведет буквально {$начало..$конец}

Причина в порядке обработки. Оболочка разворачивает скобки самым первым шагом, ещё до того, как доберётся до подстановки переменных. К моменту, когда $начало могло бы превратиться в число, развёртывание скобок уже отработало и не нашло корректного диапазона - ведь вместо чисел там стояли символы доллара. Это та самая ранняя позиция в конвейере разбора, что давала скорость, теперь оборачивается ограничением.

Когда границы диапазона лежат в переменных, выбор падает на seq, которая переменные понимает, либо на арифметический цикл в стиле C.

# Через seq - переменные работают
for i in $(seq $начало $конец); do echo $i; done

# Через арифметический цикл - без внешнего процесса
for ((i=начало; i<=конец; i++)); do echo $i; done

Существует и обходной приём через двойное вычисление командной строки командой eval, заставляющий скобки развернуться уже после подстановки переменных. Но он требует аккуратного экранирования всей строки, чтобы не словить лишних подстановок, и потому считается крайней мерой, а не повседневным решением.

Когда тянуться к скобкам, а когда к альтернативам

Практический итог укладывается в несколько правил. Brace expansion - первый выбор для генерации фиксированных последовательностей с известными заранее границами: создать пачку пронумерованных файлов, наплодить каталоги, собрать имена по шаблону, сделать резервную копию через приём с пустым элементом. Оно встроено, быстро и не требует внешних команд.

Нулевая набивка через ведущий ноль решает проблему сортировки имён, а шаг через вторую пару точек выдаёт любые равноотстоящие числа. Декартово произведение из перемноженных скобок строит сетки имён, которые иначе пришлось бы перечислять вручную.

Граница применимости проходит ровно по переменным. Как только начало или конец диапазона приходит из переменной, скобки бессильны - их черёд в разборе наступает слишком рано. Тут эстафету принимают seq для тех же числовых рядов или арифметический цикл, который к тому же обходится без порождения процесса.

Главное, что стоит запомнить: развёртывание скобок - это работа с фиксированным текстом на самом раннем этапе разбора, а не с динамическими значениями. Стоит держать в голове эту раннюю позицию в конвейере, и поведение механизма - и его молниеносность, и его слепоту к переменным - перестаёт удивлять. Оболочка раскрывает скобки прежде, чем узнаёт значения переменных, и вся практика выстраивается вокруг этого простого факта.