Опубликован: 22.06.2005 | Уровень: для всех | Доступ: свободно | ВУЗ: Компания IBM
Лекция 8:

Возможности командной оболочки

< Лекция 7 || Лекция 8: 123456 || Лекция 9 >

Генерация имен файлов

Достраивание очень удобно, когда цель пользователя - задать один конкретный файл в командной строке. Если же нужно работать сразу с несколькими файлами - например, для перемещения их в другой каталог с помощью mv, достраивание не помогает. Необходим способ задать одно "общее" имя для группы файлов, с которыми будет работать команда. В подавляющем большинстве случаев это можно сделать при помощи шаблона.

Шаблоны

Шаблон в командном интерпретаторе используется примерно в тех же целях, что и регулярное выражение, упомянутое в лекции 7: для поиска строк определенной структуры среди множества разнообразных строк. В отличие от регулярного выражения, шаблон всегда примеряется к строке целиком, кроме того, он устроен значительно проще (а значит, и беднее).

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

  • Шаблону, состоящему только из обычных символов, соответствует единственная строка, состоящая из тех же символов в том же порядке. Например, шаблону " 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 ").

Шаблон (pattern) - строка специального формата, используемая в процедурах текстового поиска. Говорят, что строка соответствует шаблону, если можно по определенным правилам каждому символу строки поставить в соответствие символ шаблона. В 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*
Пример 8.7. Использование шаблона в командной строке

Мефодий, как это случается с новичками, немедленно натолкнулся на несколько "подводных камней", неизбежных в ситуации, когда конечный результат неизвестен. Только первая команда сработала не вопреки его ожиданиям: шаблон " .bash* " был превращен командной оболочкой в список файлов, начинающихся на .bash, этот список получила в качестве параметров командной строки ls, после чего честно его вывела. С " /bin/e* " Мефодию повезло - этот шаблон превратился в список файлов из каталога /bin, начинающихся на " e ", и первым файлом в списке оказалась безобидная утилита /bin/echo. Поскольку в командной строке ничего, кроме шаблона, не было, именно строка /bin/echo была воспринята оболочкой в качестве команды4Можно вспомнить про нулевой параметр командной строки, обсуждавшийся в лекции 5., которой (в качестве параметров ) были переданы остальные элементы списка - /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 - то есть подкаталоги другого уровня вложенности - по стандартным правилам сделать нельзя5 Генерация имен файлов в " 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.8. Экранирование специальных символов и обработка "пустых" шаблонов

Мефодий заметил, что шаблон, которому не соответствует ни одно имя файла, bash раскрывать не стал, как если бы все " * " в нем были экранированы. В самом деле, какое из двух зол меньше: изменять интерпретацию спецсимволов в зависимости от содержимого каталога, или сохранять логику интерпретации с риском превратить команду с параметрами в команду без параметров? Если бы, допустим, шаблон, которому не нашлось соответствия, попросту удалялся, то команда ls *y* превратилась бы в ls и неожиданно выдала бы содержимое всего каталога. Авторы bash (как и Стивен Борн, автор самой первой командной оболочки - sh ) выбрали более непоследовательный, но и более безопасный первый способ6Авторы zsh пошли по другому пути: в этой версии shell использование шаблона, которому не соответствует ни одно имя файла, приводит к ошибке, и соответствующая команда не выполняется..

Лишить специальные символы их специального значения можно и другим способом. В лекции 2 было рассказано, что разделители (пробелы, символы табуляции и символы перевода строки) перестают восприниматься как таковые, если часть командной строки, их содержащую, окружить двойными или одинарными кавычками. В кавычках перестает "работать" и генерация имен (как это видно из примера), и интерпретация других специальных символов. Двойные кавычки, однако, допускают выполнение подстановок переменной окружения и результата работы команды, описанных в двух следующих разделах.

< Лекция 7 || Лекция 8: 123456 || Лекция 9 >
Аягоз Имансакипова
Аягоз Имансакипова
Тимур Булатов
Тимур Булатов

С момента выхода курса прошло достаточно много времени, и хотелось бы понимать, насколько курс является актуальным на сегодняшний день.