[[email protected] ~]$ grep -L bzip dirlist*.txt
dirlist-sbin.txt
dirlist-usr-bin.txt
dirlist-usr-sbin.txt
Метасимволы и литералы
Несмотря на то что пока это не очевидно, во всех своих попытках поиска с помощью grep мы использовали регулярные выражения, хотя и очень простые. Регулярное выражение bzip, к примеру, означает, что ему соответствуют только строки в файлах, содержащие не менее четырех символов и среди этих символов присутствуют символы b, z, i и p, следующие именно в таком порядке и между ними отсутствуют какие-либо другие символы. Символы в строке bzip — это литеральные символы, то есть они соответствуют сами себе. Помимо литералов регулярные выражения могут содержать метасимволы, они используются для определения более сложных критериев сопоставления. К метасимволам регулярных выражений относятся следующие символы:
^ $ . [ ] { } - ? * + ( ) |
Все остальные символы считаются литералами. Впрочем, в некоторых случаях символ обратного слеша используется для создания метапоследовательностей, а также для экранирования метасимволов, чтобы они могли интерпретироваться как литералы, а не как метасимволы.
ПРИМЕЧАНИЕ
Как видите, многие метасимволы регулярных выражений имеют также специальное значение для механизма подстановки командной оболочки. Поэтому, передавая регулярные выражения с метасимволами в виде аргументов командной строки, следует заключать их в кавычки, чтобы предотвратить попытки командной оболочки выполнить подстановку вместо них.
Любой символ
Первый метасимвол, который мы рассмотрим, — это символ точки, соответствующий любому символу. Если включить его в регулярное выражение, он будет соответствовать любому символу в данной позиции. Например:
[[email protected] ~]$ grep -h '.zip' dirlist*.txt
bunzip2
bzip2
bzip2recover
gunzip
gzip
funzip
gpg-zip
preunzip
prezip
prezip-bin
unzip
unzipsfx
Здесь выполнен поиск в наших файлах совпадений с регулярным выражением .zip. В полученных результатах имеется пара важных моментов, которые необходимо отметить. Обратите внимание, что программа zip не была найдена. Это объясняется включением в регулярное выражение метасимвола точки, увеличившим длину обязательного совпадения до четырех символов; так как в имени программы zip всего три символа, оно не было найдено. Кроме того, если бы в наших списках имелись имена файлов с расширением .zip, они также были бы найдены, потому что символ точки в расширении файла интерпретировался бы как «любой символ».
Якоря
Символ крышки (^) и знак доллара ($) в регулярных выражениях интерпретируются как якоря. Это означает, что в их присутствии совпадение с регулярным выражением возможно, только если совпадение будет найдено в начале строки (^) или в ее конце ($).
[[email protected] ~]$ grep -h '^zip' dirlist*.txt
zip
zipcloak
zipgrep
zipinfo
zipnote
zipsplit
[[email protected] ~]$ grep -h 'zip$' dirlist*.txt
gunzip
gzip
funzip
gpg-zip
preunzip
prezip
unzip
zip
[[email protected] ~]$ grep -h '^zip$' dirlist*.txt
zip
Здесь выполняется поиск в списке файлов строки zip, находящейся в начале строки, в конце строки и занимающей всю строку, от начала до конца. Обратите внимание, что регулярное выражение ^$ (начало и конец без каких-либо символов между ними) будет соответствовать пустым строкам.
в помощь любителям кроссвордов
Моя супруга обожает разгадывать кроссворды и иногда просит меня помочь с ответом на какой-нибудь вопрос. Например: «Слово из пяти букв, третья j, последняя r, которое означает...» Подобные вопросы навели меня на размышления.
Знаете ли вы, что в вашей системе Linux имеется словарь? Загляните в каталог /usr/share/dict, и вы обнаружите там один или несколько словарей. Файлы словарей, находящиеся в каталоге, — это обычные длинные списки слов, по одному в строке, упорядоченные по алфавиту. В моей системе файл words содержит больше 98 500 слов. Найти возможные ответы на вопрос в кроссворде можно с помощью следующей команды:
[[email protected] ~]$ grep -i '^..j.r$' /usr/share/dict/words
Major
major
Это регулярное выражение помогает найти в файле словаря все слова, длиной в пять букв, где третья буква — j и последняя — r.
Выражения в квадратных скобках и классы символов
В дополнение к возможности описать в регулярном выражении совпадение с любым символом в заданной позиции с помощью выражения в квадратных скобках можно также описать совпадение с одним символом из определенного множества. Выражение в квадратных скобках помогает определить множество символов (включая символы, которые иначе интерпретировались бы как метасимволы), которые находятся в данной позиции. В следующем примере используется множество из двух символов, благодаря которому обнаруживаются соответствия с последовательностями bzip и gzip:
[[email protected] ~]$ grep -h '[bg]zip' dirlist*.txt
bzip2
bzip2recover
gzip
Множество может содержать любое число символов. Метасимволы, заключенные в квадратные скобки, теряют свое специальное значение. Лишь два метасимвола интерпретируются особым образом, но при этом они имеют иной смысл. Первый — символ крышки (^), который используется для обозначения отрицания; второй — дефис (-), который используется для обозначения диапазона символов.
Отрицание
Если сразу после открывающей квадратной скобки стоит символ крышки (^), остальные символы множества интерпретируются как недопустимые в данной позиции. Проверим это, изменив предыдущий пример:
[[email protected] ~]$ grep -h '[^bg]zip' dirlist*.txt
bunzip2
gunzip
funzip
gpg-zip
preunzip
prezip
prezip-bin
unzip
unzipsfx
Включив отрицание, мы получили список файлов, имена которых содержат последовательность zip, которой предшествует любой символ, кроме b или g. Обратите внимание, что файл zip не был найден. Символ отрицания не отменяет необходимости присутствия символа в заданной позиции, он лишь требует, чтобы символ в этой позиции не принадлежал указанному множеству.
Символ крышки обозначает операцию отрицания, только если является первым символом в выражении в квадратных скобках; в противном случае он теряет свое специальное значение и превращается в обычный символ.
Традиционные диапазоны символов
Если необходимо сконструировать регулярное выражение, которое находило бы в наших списках все файлы с именами, начинающимися с заглавной буквы, это можно выполнить следующим образом:
[[email protected] ~]$ grep -h '^[ABCDEFGHIJKLMNOPQRSTUVWXZY]' dirlist*.txt
Достаточно просто поместить 26 букв в верхнем регистре в выражение в квадратных скобках. Но необходимость ввода всех этих символов вызывает некоторое беспокойство, поэтому предусмотрен другой способ:
[[email protected] ~]$ grep -h '^[A-Z]' dirlist*.txt
MAKEDEV
ControlPanel
GET
HEAD
POST
X
X11
Xorg
MAKEFLOPPIES
NetworkManager
NetworkManagerDispatcher
Мы сократили множество с 26 буквами до 3-символьного диапазона. Так можно выразить любой диапазон символов и даже несколько диапазонов, например, для поиска имен файлов, начинающихся с буквы или цифры:
[[email protected] ~]$ grep -h '^[A-Za-z0-9]' dirlist*.txt
Как следует из примеров, символ дефиса получает в диапазонах специальное значение, поэтому возникает вопрос: как включить дефис в выражение в квадратных скобках, чтобы он интерпретировался как обычный символ? Для этого достаточно поставить его в начало выражения. Например: