Продолжаем серию постов по спецсимволам оболочки: (), $(), []. Сегодня говорим про расширение фигурных скобок (brace expansion).
Символы {} позволяют развернуть выражение внутри в последовательность аргументов:
$ echo {1..10}
1 2 3 4 5 6 7 8 9 10
$ echo {10..1}
10 9 8 7 6 5 4 3 2 1
Внутрь скобок может быть добавлен опциональный 3 параметр. С ним выражение {x..y..z} генерирует значения от x до y с шагом z:
$ echo {1..501..100}
1 101 201 301 401 501
$ echo {501..1..100}
501 401 301 201 101 1
Расширение {}, в отличие от команды seq, может создавать последовательность букв:
$ echo {A..P}
A B C D E F G H I J K L M N O P
При желании, можем добавить шаг, например, чтобы выводить символ через 1 в диапазоне от A до P:
$ echo {A..P..2}
A C E G I K M O
Использование перечислений
Если нам не требуется вычисление последовательности, можно поместить внутрь скобок перечисление:
$ mkdir ~/Sources/{test1,test2,test3}
Вариант практического применения данной техники - получение различий между файлами:
$ diff {orig,patched}/path/to/prog.c
Прогерам бывает приходится составлять пачи по результатам команды diff и расширение тут очень кстати - не нужно дважды вводить почти идентичный путь.
Немного отойдем от общего вида паттерна, оставим 1 аргумент и создадим бэкап файла:
$ cp -p file.txt{,.bak}
$ ls
file.txt file.txt.bak
Если перед запятой отсутствует значение, подстановка в этом месте пропускается, но вывод, при наличии текста вне {}, выполняется:
$ echo file.txt{,.bak}
file.txt file.txt.bak
Предупреждение: не ставьте пробелы внутри скобок иначе расширение не отработает:
$ echo file{1.. 2}.txt
file{1.. 2}.txt
Изменение формата вывода
Как можно заметить из примеров выше, вывод всегда записывается в виде одной строки, разделенной пробелами. Изменить это можно, перенаправив его другим командам, таким как tr:
$ echo {A..P..2} | tr -d " "
ACEGIKMO
Теперь можно создать псевдоним, который печатает n букву английского алфавита:
$ alias nth="echo {A..Z} | tr -d ' ' | cut -c"
$ nth 10
J
Фигурные скобки vs квадратные
Квадратные скобки — это оператор сопоставления имен файлов с шаблоном. Фигурные, в свою очередь, не имеют к файлам никакого отношения. Они просто просчитывают и возвращают последовательность значений.
К примеру, у нас в каталоге содержится 4 файла:
$ ls
file1.txt file2.txt file3.txt file4.txt
Используем сопоставление по шаблону в диапазоне от 1 до 9:
$ ls file[1-9].txt
file1.txt file2.txt file3.txt file4.txt
Все отработало без ошибок, оболочка нашла файлы, которые подходят под шаблон и передала их в качестве аргументов.
Теперь в этом же диапазоне используем расширение {}:
$ ls file{1..9}.txt
ls: cannot access 'file5.txt': No such file or directory
ls: cannot access 'file6.txt': No such file or directory
...
file1.txt file2.txt file3.txt file4.txt
Оболочка без разбора подставила всю последовательность. Что-то нашлось, что-то нет.
Группировка расширений
Спокойно можно использовать группу символов {} в 1 выражении, чтобы, например, создать несколько файлов с разными расширеними:
$ touch {a,b,c}.{hpp,cpp}
$ ls
a.cpp a.hpp b.cpp b.hpp c.cpp c.hpp
Bash берёт каждую букву из первого набора и комбинирует её со всеми вариантами второго.
Либо еще опция - использовать вложенность. Бывает полезно при создании группы подкаталогов:
$ mkdir -p {source,help{/pages,/yelp,/images}}
$ tree
.
├── help
│ ├── images
│ ├── pages
│ └── yelp
└── source
LinuxCamp
