Рубрики
find \ grep \ поиск

Регулярные выражения

Ссылка:

http://habrahabr.ru/post/102442/

Что такое «регулярное выражение»?

Регулярное выражение (по англ. regular expression, сокр. «regexp» или «regex», в отечестве иногда зовется «регулярка» — прим. пер.) 
— это особый синтаксис используемый для описания текстовых шаблонов. 
В Linux-системах регулярные выражения широко используются для поиска в тексте по шаблону, а также для операций поиска и замены на текстовых потоках.

В сравнении с глоббингом

Как только мы начнем рассматривать регулярные выражения, возможно вы обратите внимание, 
что их синтаксис очень похож на синтаксис подстановки имен файлов (globbing), который мы рассматривали в первой части. 
Однако, не стоит заблуждаться, эта схожесть очень поверхностна. 
Регулярные выражения и глоббинг-шаблоны, даже когда они выглядят похоже, принципиально разные вещи.

Простая подстрока


После этого предостережения, давайте рассмотрим самое основное в регулярных выражениях, 
простейшую подстроку. Для этого мы воспользуемся «grep», командой, которая сканирует содержимое 
файла согласно заданному регулярному выражению. grep выводит каждую строчку, которая совпадает 
с регулярным выражением, игнорируя остальные:
$ grep bash /etc/passwd
operator:x:11:0:operator:/root:/bin/bash
root:x:0:0::/root:/bin/bash
ftp:x:40:1::/home/ftp:/bin/bash

Выше, первый параметр для grep, это regex; второй — имя файла. grep считывал каждую строчку 
из /etc/passwd и прикладывал на нее простую regex-подстроку «bash» в поисках совпадения. 
Если совпадение обнаруживалось, то grep выводил всю строку целиком; в противном случае, строка игнорировалась.


Понимание простой подстроки

В общем случае, если вы ищите подстроку, вы просто можете указать её буквально, не используя 
каких-либо «специальных» символов. Вам понадобиться особо позаботиться, только если ваша 
подстрока содержит +, ., *, [, ] или \, в этом случае эти символы должны быть экранированы 
обратным слешем, а подстрока заключаться в кавычки. 
Вот несколько примеров регулярных выражений в виде простой подстроки:
/tmp (поиск строки /tmp)
"\[box\]" (поиск строки [box])
"\*funny\*" (поиск строки *funny*)
«ld\.so» (поиск строки ld.so)

Мета символы

С помощью регулярных выражений используя мета символы возможно осуществлять гораздо более сложный поиск, 
чем в примерах, которые недавно рассматривали. Один из таких мета символов "." (точка), 
который совпадает с любым единичным символом:
$ grep dev.sda /etc/fstab
/dev/sda3       /               reiserfs        noatime,ro 1 1
/dev/sda1       /boot           reiserfs        noauto,noatime,notail 1 2
/dev/sda2       swap            swap            sw 0 0
#/dev/sda4      /mnt/extra      reiserfs        noatime,rw 1 1

В этом примере текст dev.sda не появляется буквально ни в одной из строчек из /etc/fstab. 
Однако, grep сканирует его не буквально по строке dev.sda, а по dev.sda шаблону. Запомните, 
что "." будет соответствовать любому единичному символу. Как вы видите, мета символ "." 
функционально эквивалентен тому, как работает мета символ "?" в glob-подстановках.

Использование []

Если мы хотим задать символ конкретнее, чем это делает ".", то можем использовать [ и ] 
(квадратные скобки), чтобы указать подмножество символов для сопоставления:
$ grep dev.sda[12] /etc/fstab
/dev/sda1       /boot           reiserfs        noauto,noatime,notail 1 2
/dev/sda2       swap            swap            sw 0 0

Как вы заметили, в частности, данная синтаксическая конструкция работает идентично конструкции "[]" 
при glob-подстановке имен файлов. Опять же, в этом заключается одна из неоднозначностей 
в изучении регулярных выражений: синтаксис похожий, но не идентичный синтаксису glob-подстановок, что сбивает с толку.

Использование [^]

Вы можете обратить значение квадратных скобок поместив ^ сразу после [. В этому случае скобки 
будут соответствовать любому символу который НЕ перечислен внутри них. И опять, заметьте что [^] 
мы используем с регулярными выражением, а [!] с glob:
$ grep dev.hda[^12] /etc/fstab
/dev/hda3       /               reiserfs        noatime,ro 1 1
#/dev/hda4      /mnt/extra      reiserfs        noatime,rw 1 1


Отличающийся синтаксис

Очень важно отметить, что синтаксис внутри квадратных скобок коренным образом отличается от остальной 
части регулярного выражения. К примеру, если вы поместите "." внутрь квадратных скобок, это позволит 
квадратным скобкам совпадать с "." буквально, также как 1 и 2 в примере выше. Для сравнения, "." 
помещенная вне квадратных скобок, будет интерпретирована как мета символ, если не приставить "\". 
Мы можем получить выгоду из данного факта для вывода строк из /etc/fstab которые содержат строку dev.sda, 
как она записана:
$ grep dev[.]sda /etc/fstab

Также, мы могли бы набрать:
$ grep "dev\.sda" /etc/fstab

Эти регулярные выражения вероятно не удовлетворяют 
ни одной строчке из вашего /etc/fstab файла.

Мета символ *

Некоторые мета символы сами по себе не соответствуют ничему, но изменяют значение предыдущего символа. 
Один из таких символов, это * (звездочка), который используется для сопоставления нулевому или большему 
числу повторений предшествующего символа. Заметьте, это значит, что * имеет другое значение в регулярках, 
нежели в глоббинге. Вот несколько примеров, и обратите особое внимание на те случаи где сопоставление 
регулярных выражений отличается от glob-подстановок:
ab*c совпадает с «abbbbc», но не с «abqc» (в случае glob-подстановки, обе строчки будут удовлетворять шаблону. Вы уже поняли почему?)
ab*c совпадает с «abc», но не с «abbqbbc» (опять же, при glob-подстановке, шаблон сопоставим с обоими строчками)
ab*c совпадает с «ac», но не с «cba» (в случае глоббинга, ни «ac», ни «cba» не удовлетворяют шаблону)
b[cq]*e совпадает с «bqe» и с «be» (glob-подстановке удовлетворяет «bqe», но не «be»)
b[cq]*e совпадает с «bccqqe», но не с «bccc» (при глоббинге шаблон точно так же совпадет с первым, но не со вторым)
b[cq]*e совпадает с «bqqcce», но не с «cqe» (так же и при glob-подстановке)
b[cq]*e удовлетворяет «bbbeee» (но не в случае глоббинга)
.* сопоставим с любой строкой (glob-подстановке удовлетворяют только строки начинающиеся с ".")
foo.* совпадет с любой подстрокой начинающийся с «foo» (в случае glob-подстановки 
этот шаблон будет совпадать со строками, начинающимися с четырех символов «foo.»)

Итак, повторим для закрепления: строчка «ac» подходит под регулярное выражение «ab*c» потому, 
что звездочка также позволяет повторение предшествующего выражения (b) ноль раз. И опять, ценно отметить для себя, 
что мета символ * в регулярках интерпретируется совершенно иначе, нежели символ * в glob-подстановках.

Начало и конец строки

Последние метасимволы, что мы детально рассмотрим, это ^ и $, которые используются для сопоставления началу 
и концу строки, соответственно. Воспользовавшись ^ в начале вашего regex, вы «прикрепите» ваш шаблон к началу строки. 
В следующем примере, мы используем регулярное выражение ^#, 
которое удовлетворяет любой строке начинающийся с символа #:
$ grep ^# /etc/fstab
# /etc/fstab: static file system information.
#

Полно строчные регулярки

^ и $ можно комбинировать, для сопоставлений со всей строкой целиком. 
Например, нижеследующая регулярка будет соответствовать строкам начинающимся с символа #, 
а заканчивающимся символом ".", при произвольном количестве символов между ними:

$ grep '^#.*\.$' /etc/fstab
# /etc/fstab: static file system information.

В примере выше мы заключили наше регулярное выражение в одиночные кавычки, 
чтобы предотвратить интерпретирование символа $ командной оболочкой. 
Без одиночных кавычек $ исчез бы из нашей регулярки еще даже до того, как grep мог его увидеть.