Достраивание очень удобно, когда цель пользователя — задать один конкретный файл в командной строке. Если же нужно работать сразу с несколькими файлами — например для перемещения их в другой каталог с помощью 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 использование шаблона, которому не соответствует ни одно имя файла, приводит к ошибке, и соответствующая команда не выполняется.
Лишить специальные символы их специального значения можно и другим способом. В лекции Терминал и командная строка было рассказано, что разделители (пробелы, символы табуляции и символы перевода строки) перестают восприниматься таковыми, если часть командной строки, их содержащую, окружить двойными или одинарными кавычками. В кавычках перестаёт «работать» и генерация имён (как это видно из примера), и интерпретация других специальных символов. Двойные кавычки, однако, допускают выполнение подстановок переменной окружения и результата работы команды, описанных в двух следующих разделах.