Достраивание очень удобно, когда цель пользователя — задать один конкретный файл в командной строке. Если же нужно работать сразу с несколькими файлами — например для перемещения их в другой каталог с помощью mv, достраивание не помогает. Необходим способ задать одно «общее» имя для группы файлов, с которыми будет работать команда. В подавляющем большинстве случаев это можно сделать при помощи шаблона.
Шаблоны
Шаблон в командном интерпретаторе используется примерно в тех же целях, что и регулярное выражение, упомянутое в лекции Работа с текстовыми данными: для поиска строк определённй структуры среди множества разнообразных строк. В отличие от регулярного выражения, шаблон всегда примеряется к строке целиком, кроме того, он устроен значительно проще (а значит, и беднее).
Символы в шаблоне разделяются на обычные и специальные. Обычные символы соответствуют таким же символам в строке, а специальные обрабатываются особым образом:
- Шаблону, состоящему только из обычных символов, соответствует единственная строка, состоящая из тех же символов в том же порядке. Например, шаблону «
abc» соответствует строкаabc, но неaBcилиABC, потому что большие и маленькие буквы различаются. - Шаблону, состоящему из единственного спецсимвола «
*», соответствует любая строка любой длины (в том числе и пустая). - Шаблону, состоящему из единственного спецсимвола «
?», соответствует любая строка длиной в один символ, например,a,+или@, но неabили8888. - Шаблону, состоящему из любых символов, заключённых в квадратные скобки «
[» и «]» соответствует строка длиной в один символ, причём этот символ должен встречаться среди заключённых в скобки. Например, шаблону «[bar]» соответствуют только строкиa,bиr, но неc,B,barилиab. Символы внутри скобок можно не перечислять полностью, а задавать диапазон, в начале которого стоит символ с наименьшим ASCII-кодом, затем следует «-», а затем — символ с наибольшим ASCII-кодом. Например, шаблону «[0-9a-fA-F]» соответствует одна шестнадцатеричная цифра (скажем,5,eилиC). Если после «[» в шаблоне следует «!», то ему соответствует строка из одного символа не перечисленного между скобками. - Шаблону, состоящему из нескольких частей, соответствует строка, которую можно разбить на столько же подстрок (возможно, пустых), причём первая подстрока будет отвечать первой части шаблона, вторая — второй части и т. д. Например, шаблону «
a*b?c» будут соответствовать строкиab@c(«*» соответствует пустая подстрока),a+b=cиaaabbc, но не соответствоватьabc(«?» соответствует подстрокаc, а для «c» соответствия не находится),@ab@c(нет соответствия для «a») илиaaabbbc(из трёхbпервое соответствует «b», второе — «?», а вот третье приходится на «c»).
- шаблон
- Строка специального формата, используемая в процедурах текстового поиска. Говорят, что строка соответствует шаблону, если можно по определённым правилам каждому символу строки поставить в соответствие символ шаблона. В Linux наиболее популярны шаблоны в формате командного интерпретатора и регулярные выражения.
Использование шаблонов
Шаблоны используются в нескольких конструкциях shell. Главное место их применения — командная строка. Если оболочка видит в командной строке шаблон, она немедленно заменяет его на список файлов, имена которых ему соответстуют. Команда, которая затем вызывается, получает в качестве параметров список файлов уже безо всяких шаблонов, как если бы этот список пользователь ввёл вручную. Эта способность командного интерпретатора называется генерацией имён файлов.
[methody@localhost methody]$ ls .bash*
.bash_history .bash_logout .bash_profile .bashrc
[methody@localhost methody]$ /bin/e*
/bin/ed /bin/egrep /bin/ex
[methody@localhost methody]$ ls *a*
-filename-with-
[methody@localhost methody]$ ls -dF *[ao]*
Documents/ examples/ loop to.sort*
Пример 7. Использование шаблона в командной строке
Мефодий, как это случается с новичками, немедленно натолкнулся на несколько «подводных камней», неизбежных в ситуации, когда конечный результат неизвестен. Только первая команда сработала не вопреки его ожиданиям: шаблон «.bash*» был превращён командной оболочкой в список файлов, начинающихся на .bash, этот список получила в качестве параметров командной строки ls, после чего честно его вывела. С «/bin/e*» Мефодию повезло — этот шаблон превратился в список файлов из каталога /bin, начинающихся на «e», и первым файлом в списке оказалась безобидная утилита /bin/echo. Поскольку в командной строке ничего, кроме шаблона, не было, именно строка /bin/echo была воспринята оболочкой в качестве команды, которой — в качестве параметров — были переданы остальные элементы списка — /bin/ed, /bin/egrep и /bin/ex.
Можно вспомнить про нулевой параметр командной строки, обсуждавшийся в лекции Доступ процессов к файлам и каталогам.
Что же касается ls *a*, то, по расчётам Мефодия, эта команда должна была выдать список файлов в текущем каталоге, имя которых содержит «a». Вместо этого на экран вывелось имя файла из подкаталога examples... Впрочем, никакой чёрной магии тут нет. Во-первых, имена файлов вида «.bash*» хотя и содержат «a», но начинаются на точку, и, стало быть, считаются скрытыми. Скрытые файлы попадают в результат генерации имён только если точка в начале указана явно (как в первой команде примера). Поэтому по шаблону «*a*» в домашнем каталоге Мефодия bash нашёл только подкаталог с именем examples, его-то он и передал в качестве параметра утилите ls. Что вывелось на экран в результате образовавшейся команды ls examples? Конечно, содержимое каталога. Шаблон в последней команде из примера, «*[ao]*», был превращён в список файлов, чьи имена содержат «a» или «o» — Documents examples loop to.sort, а ключ «-d» потребовал у ls показывать информацию о каталогах, а не об их содержимом. В соответствии с ключом «-F», ls расставил «/» после каталогов и «*» после исполняемых файлов.
Ещё одно отличие генерации имён от стандартной обработки шаблона — в том, что символ «/», разделяющий элементы пути, никогда не ставится в соответствие «*» или диапазону. Происходит это не потому, что искажён алгоритм, а потому, что при генерации имён шаблон применяется именно к элементу пути, внутри которого уже нет «/». Например, получить список файлов, которые находятся в каталогах /usr/bin и /usr/sbin и содержат подстроку «ppp» в имени, можно с помощью шаблона «/usr/*bin/*ppp*». Однако одного шаблона, который бы включал в этот список ещё и каталоги /bin и /sbin — то есть подкаталоги другого уровня вложенности — по стандартным правилам сделать нельзя.
Генерация имён файлов в zsh предусматривает специальный шаблон «**», которому соответствуют подстроки с любым количеством «/». Пользоваться им следует крайне осторожно, понимая, что при генерации имён по такому шаблону выполняется операция, аналогичная не ls, а ls -R или find. Так, использование «/**» в начале шаблона вызовет просмотр всей файловой системы!
Если перед любым специальным символом стоит «\», этот символ лишается специального значения, экранируется: пара «\символ» заменяется командным интерпретатором на «символ» и передаётся в командную строку безо всякой дальнейшей обработки:
[methody@localhost methody]$ echo *o*
Documents loop to.sort
[methody@localhost methody]$ echo \*o\*
*o*
[methody@localhost methody]$ echo "*o*"
*o*
[methody@localhost methody]$ echo *y*
*y*
[methody@localhost methody]$ ls *y*
ls: *y*: No such file or directory
Пример 8. Экранирование специальных символов и обработка «пустых» шаблонов
Мефодий заметил, что шаблон, которому не соответствует ни одного имени файла, bash раскрывать не стал, как если бы все «*» в нём были экранированы. В самом деле, какое из двух зол меньшее: изменять интерпретацию спецсимволов в зависимости от содержимого каталога, или сохранять логику интерпретации с риском превратить команду с параметрами в команду без параметров? Если бы, допустим, шаблон, которому не нашлось соответствия, попросту удалялся, то команда ls *y* превратилась бы в ls и неожиданно выдала бы содержимое всего каталога. Авторы bash (как и Стивен Борн, автор самой первой командной оболочки — sh) выбрали более непоследовательный, но и более безопасный первый способ.
вторы zsh пошли по другому пути: в этой версии shell использование шаблона, которому не соответствует ни одно имя файла, приводит к ошибке, и соответствующая команда не выполняется.
Лишить специальные символы их специального значения можно и другим способом. В лекции Терминал и командная строка было рассказано, что разделители (пробелы, символы табуляции и символы перевода строки) перестают восприниматься таковыми, если часть командной строки, их содержащую, окружить двойными или одинарными кавычками. В кавычках перестаёт «работать» и генерация имён (как это видно из примера), и интерпретация других специальных символов. Двойные кавычки, однако, допускают выполнение подстановок переменной окружения и результата работы команды, описанных в двух следующих разделах.