Регулярные выражения и команда grep. Использование Grep и регулярных выражений для поиска текстовых шаблонов в Linux Egrep примеры регулярных выражений

Для того, чтобы полноценно обрабатывать тексты в bash-скриптах с помощью sed и awk, просто необходимо разобраться с регулярными выражениями. Реализации этого полезнейшего инструмента можно найти буквально повсюду, и хотя устроены все регулярные выражения схожим образом, основаны на одних и тех же идеях, в разных средах работа с ними имеет определённые особенности. Тут мы поговорим о регулярных выражениях, которые подходят для использования в сценариях командной строки Linux.

Этот материал задуман как введение в регулярные выражения, рассчитанное на тех, кто может совершенно не знать о том, что это такое. Поэтому начнём с самого начала.

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

У многих, когда они впервые видят регулярные выражения, сразу же возникает мысль, что перед ними бессмысленное нагромождение символов. Но это, конечно, далеко не так. Взгляните, например, на это регулярное выражение


На наш взгляд даже абсолютный новичок сходу поймёт, как оно устроено и зачем нужно:) Если же вам не вполне понятно - просто читайте дальше и всё встанет на свои места.
Регулярное выражение - это шаблон, пользуясь которым программы вроде sed или awk фильтруют тексты. В шаблонах используются обычные ASCII-символы, представляющие сами себя, и так называемые метасимволы, которые играют особую роль, например, позволяя ссылаться на некие группы символов.

Типы регулярных выражений

Реализации регулярных выражений в различных средах, например, в языках программирования вроде Java, Perl и Python, в инструментах Linux вроде sed, awk и grep, имеют определённые особенности. Эти особенности зависят от так называемых движков обработки регулярных выражений, которые занимаются интерпретацией шаблонов.
В Linux имеется два движка регулярных выражений:
  • Движок, поддерживающий стандарт POSIX Basic Regular Expression (BRE).
  • Движок, поддерживающий стандарт POSIX Extended Regular Expression (ERE).
Большинство утилит Linux соответствуют, как минимум, стандарту POSIX BRE, но некоторые утилиты (в их числе - sed) понимают лишь некое подмножество стандарта BRE. Одна из причин такого ограничения - стремление сделать такие утилиты как можно более быстрыми в деле обработки текстов.

Стандарт POSIX ERE часто реализуют в языках программирования. Он позволяет пользоваться большим количеством средств при разработке регулярных выражений. Например, это могут быть специальные последовательности символов для часто используемых шаблонов, вроде поиска в тексте отдельных слов или наборов цифр. Awk поддерживает стандарт ERE.

Существует много способов разработки регулярных выражений, зависящих и от мнения программиста, и от особенностей движка, под который их создают. Непросто писать универсальные регулярные выражения, которые сможет понять любой движок. Поэтому мы сосредоточимся на наиболее часто используемых регулярных выражениях и рассмотрим особенности их реализации для sed и awk.

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

Пожалуй, самый простой шаблон BRE представляет собой регулярное выражение для поиска точного вхождения последовательности символов в тексте. Вот как выглядит поиск строки в sed и awk:

$ echo "This is a test" | sed -n "/test/p" $ echo "This is a test" | awk "/test/{print $0}"

Поиск текста по шаблону в sed


Поиск текста по шаблону в awk

Можно заметить, что поиск заданного шаблона выполняется без учёта точного места нахождения текста в строке. Кроме того, не имеет значение и количество вхождений. После того, как регулярное выражение найдёт заданный текст в любом месте строки, строка считается подходящей и передаётся для дальнейшей обработки.

Работая с регулярными выражениями нужно учитывать то, что они чувствительны к регистру символов:

$ echo "This is a test" | awk "/Test/{print $0}" $ echo "This is a test" | awk "/test/{print $0}"

Регулярные выражения чувствительны к регистру

Первое регулярное выражение совпадений не нашло, так как слово «test», начинающееся с заглавной буквы, в тексте не встречается. Второе же, настроенное на поиск слова, написанного прописными буквами, обнаружило в потоке подходящую строку.

В регулярных выражениях можно использовать не только буквы, но и пробелы, и цифры:

$ echo "This is a test 2 again" | awk "/test 2/{print $0}"

Поиск фрагмента текста, содержащего пробелы и цифры

Пробелы воспринимаются движком регулярных выражений как обычные символы.

Специальные символы

При использовании различных символов в регулярных выражениях надо учитывать некоторые особенности. Так, существуют некоторые специальные символы, или метасимволы, использование которых в шаблоне требует особого подхода. Вот они:

.*^${}\+?|()
Если один из них нужен в шаблоне, его нужно будет экранировать с помощью обратной косой черты (обратного слэша) - \ .

Например, если в тексте нужно найти знак доллара, его надо включить в шаблон, предварив символом экранирования. Скажем, имеется файл myfile с таким текстом:

There is 10$ on my pocket
Знак доллара можно обнаружить с помощью такого шаблона:

$ awk "/\$/{print $0}" myfile

Использование в шаблоне специального символа

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

$ echo "\ is a special character" | awk "/\\/{print $0}"

Экранирование обратного слэша

Хотя прямой слэш и не входит в приведённый выше список специальных символов, попытка воспользоваться им в регулярном выражении, написанном для sed или awk, приведёт к ошибке:

$ echo "3 / 2" | awk "///{print $0}"

Неправильное использование прямого слэша в шаблоне

Если он нужен, его тоже надо экранировать:

$ echo "3 / 2" | awk "/\//{print $0}"

Экранирование прямого слэша

Якорные символы

Существуют два специальных символа для привязки шаблона к началу или к концу текстовой строки. Символ «крышка» - ^ позволяет описывать последовательности символов, которые находятся в начале текстовых строк. Если искомый шаблон окажется в другом месте строки, регулярное выражение на него не отреагирует. Выглядит использование этого символа так:

$ echo "welcome to likegeeks website" | awk "/^likegeeks/{print $0}" $ echo "likegeeks website" | awk "/^likegeeks/{print $0}"

Поиск шаблона в начале строки

Символ ^ предназначен для поиска шаблона в начале строки, при этом регистр символов так же учитывается. Посмотрим, как это отразится на обработке текстового файла:

$ awk "/^this/{print $0}" myfile


Поиск шаблона в начале строки в тексте из файла

При использовании sed, если поместить крышку где-нибудь внутри шаблона, она будет восприниматься как любой другой обычный символ:

$ echo "This ^ is a test" | sed -n "/s ^/p"

Крышка, находящаяся не в начале шаблона в sed

В awk, при использовании такого же шаблона, данный символ надо экранировать:

$ echo "This ^ is a test" | awk "/s \^/{print $0}"

Крышка, находящаяся не в начале шаблона в awk

С поиском фрагментов текста, находящихся в начале строки мы разобрались. Что, если надо найти нечто, расположенное в конце строки?

В этом нам поможет знак доллара - $ , являющийся якорным символом конца строки:

$ echo "This is a test" | awk "/test$/{print $0}"

Поиск текста, находящегося в конце строки

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

$ awk "/^this is a test$/{print $0}" myfile


Шаблон, в котором использованы специальные символы начала и конца строки

Как видно, шаблон среагировал лишь на строку, полностью соответствующую заданной последовательности символов и их расположению.

Вот как, пользуясь якорными символами, отфильтровать пустые строки:

$ awk "!/^$/{print $0}" myfile
В данном шаблоне использовал символ отрицания, восклицательный знак - ! . Благодаря использованию такого шаблона выполняется поиск строк, не содержащих ничего между началом и концом строки, а благодаря восклицательному знаку на печать выводятся лишь строки, которые не соответствуют этому шаблону.

Символ «точка»

Точка используется для поиска любого одиночного символа, за исключением символа перевода строки. Передадим такому регулярному выражению файл myfile , содержимое которого приведено ниже:

$ awk "/.st/{print $0}" myfile


Использование точки в регулярных выражениях

Как видно по выведенным данным, шаблону соответствуют лишь первые две строки из файла, так как они содержат последовательность символов «st», предварённую ещё одним символом, в то время как третья строка подходящей последовательности не содержит, а в четвёртой она есть, но находится в самом начале строки.

Классы символов

Точка соответствует любому одиночному символу, но что если нужно более гибко ограничить набор искомых символов? В подобной ситуации можно воспользоваться классами символов.

Благодаря такому подходу можно организовать поиск любого символа из заданного набора. Для описания класса символов используются квадратные скобки - :

$ awk "/th/{print $0}" myfile


Описание класса символов в регулярном выражении

Тут мы ищем последовательность символов «th», перед которой есть символ «o» или символ «i».

Классы оказываются очень кстати, если выполняется поиск слов, которые могут начинаться как с прописной, так и со строчной буквы:

$ echo "this is a test" | awk "/his is a test/{print $0}" $ echo "This is a test" | awk "/his is a test/{print $0}"

Поиск слов, которые могут начинаться со строчной или прописной буквы

Классы символов не ограничены буквами. Тут можно использовать и другие символы. Нельзя заранее сказать, в какой ситуации понадобятся классы - всё зависит от решаемой задачи.

Отрицание классов символов

Классы символов можно использовать и для решения задачи, обратной описанной выше. А именно, вместо поиска символов, входящих в класс, можно организовать поиск всего, что в класс не входит. Для того, чтобы добиться такого поведения регулярного выражения, перед списком символов класса нужно поместить знак ^ . Выглядит это так:

$ awk "/[^oi]th/{print $0}" myfile


Поиск символов, не входящих в класс

В данном случае будут найдены последовательности символов «th», перед которыми нет ни «o», ни «i».

Диапазоны символов

В символьных классах можно описывать диапазоны символов, используя тире:

$ awk "/st/{print $0}" myfile


Описание диапазона символов в символьном классе

В данном примере регулярное выражение реагирует на последовательность символов «st», перед которой находится любой символ, расположенный, в алфавитном порядке, между символами «e» и «p».

Диапазоны можно создавать и из чисел:

$ echo "123" | awk "//" $ echo "12a" | awk "//"

Регулярное выражение для поиска трёх любых чисел

В класс символов могут входить несколько диапазонов:

$ awk "/st/{print $0}" myfile


Класс символов, состоящий из нескольких диапазонов

Данное регулярное выражение найдёт все последовательности «st», перед которыми есть символы из диапазонов a-f и m-z .

Специальные классы символов

В BRE имеются специальные классы символов, которые можно использовать при написании регулярных выражений:
  • [[:alpha:]] - соответствует любому алфавитному символу, записанному в верхнем или нижнем регистре.
  • [[:alnum:]] - соответствует любому алфавитно-цифровому символу, а именно - символам в диапазонах 0-9 , A-Z , a-z .
  • [[:blank:]] - соответствует пробелу и знаку табуляции.
  • [[:digit:]] - любой цифровой символ от 0 до 9 .
  • [[:upper:]] - алфавитные символы в верхнем регистре - A-Z .
  • [[:lower:]] - алфавитные символы в нижнем регистре - a-z .
  • [[:print:]] - соответствует любому печатаемому символу.
  • [[:punct:]] - соответствует знакам препинания.
  • [[:space:]] - пробельные символы, в частности - пробел, знак табуляции, символы NL , FF , VT , CR .
Использовать специальные классы в шаблонах можно так:

$ echo "abc" | awk "/[[:alpha:]]/{print $0}" $ echo "abc" | awk "/[[:digit:]]/{print $0}" $ echo "abc123" | awk "/[[:digit:]]/{print $0}"


Специальные классы символов в регулярных выражениях

Символ «звёздочка»

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

$ echo "test" | awk "/tes*t/{print $0}" $ echo "tessst" | awk "/tes*t/{print $0}"


Использование символа * в регулярных выражениях

Этот шаблонный символ обычно используют для работы со словами, в которых постоянно встречаются опечатки, или для слов, допускающих разные варианты корректного написания:

$ echo "I like green color" | awk "/colou*r/{print $0}" $ echo "I like green colour " | awk "/colou*r/{print $0}"

Поиск слова, имеющего разные варианты написания

В этом примере одно и то же регулярное выражение реагирует и на слово «color», и на слово «colour». Это так благодаря тому, что символ «u», после которого стоит звёздочка, может либо отсутствовать, либо встречаться несколько раз подряд.

Ещё одна полезная возможность, вытекающая из особенностей символа звёздочки, заключается в комбинировании его с точкой. Такая комбинация позволяет регулярному выражению реагировать на любое количество любых символов:

$ awk "/this.*test/{print $0}" myfile


Шаблон, реагирующий на любое количество любых символов

В данном случае неважно сколько и каких символов находится между словами «this» и «test».

Звёздочку можно использовать и с классами символов:

$ echo "st" | awk "/s*t/{print $0}" $ echo "sat" | awk "/s*t/{print $0}" $ echo "set" | awk "/s*t/{print $0}"


Использование звёздочки с классами символов

Во всех трёх примерах регулярное выражение срабатывает, так как звёздочка после класса символов означает, что если будет найдено любое количество символов «a» или «e», а также если их найти не удастся, строка будет соответствовать заданному шаблону.

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

Шаблоны стандарта POSIX ERE, которые поддерживают некоторые утилиты Linux, могут содержать дополнительные символы. Как уже было сказано, awk поддерживает этот стандарт, а вот sed - нет.

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

▍Вопросительный знак

Вопросительный знак указывает на то, что предшествующий символ может встретиться в тексте один раз или не встретиться вовсе. Этот символ - один из метасимволов повторений. Вот несколько примеров:

$ echo "tet" | awk "/tes?t/{print $0}" $ echo "test" | awk "/tes?t/{print $0}" $ echo "tesst" | awk "/tes?t/{print $0}"


Вопросительный знак в регулярных выражениях

Как видно, в третьем случае буква «s» встречается дважды, поэтому на слово «tesst» регулярное выражение не реагирует.

Вопросительный знак можно использовать и с классами символов:

$ echo "tst" | awk "/t?st/{print $0}" $ echo "test" | awk "/t?st/{print $0}" $ echo "tast" | awk "/t?st/{print $0}" $ echo "taest" | awk "/t?st/{print $0}" $ echo "teest" | awk "/t?st/{print $0}"


Вопросительный знак и классы символов

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

▍Символ «плюс»

Символ «плюс» в шаблоне указывает на то, что регулярное выражение обнаружит искомое в том случае, если предшествующий символ встретится в тексте один или более раз. При этом на отсутствие символа такая конструкция реагировать не будет:

$ echo "test" | awk "/te+st/{print $0}" $ echo "teest" | awk "/te+st/{print $0}" $ echo "tst" | awk "/te+st/{print $0}"


Символ «плюс» в регулярных выражениях

В данном примере, если символа «e» в слове нет, движок регулярных выражений не найдёт в тексте соответствий шаблону. Символ «плюс» работает и с классами символов - этим он похож на звёздочку и вопросительный знак:

$ echo "tst" | awk "/t+st/{print $0}" $ echo "test" | awk "/t+st/{print $0}" $ echo "teast" | awk "/t+st/{print $0}" $ echo "teeast" | awk "/t+st/{print $0}"


Знак «плюс» и классы символов

В данном случае если в строке имеется любой символ из класса, текст будет сочтён соответствующим шаблону.

▍Фигурные скобки

Фигурные скобки, которыми можно пользоваться в ERE-шаблонах, похожи на символы, рассмотренные выше, но они позволяют точнее задавать необходимое число вхождений предшествующего им символа. Указывать ограничение можно в двух форматах:
  • n - число, задающее точное число искомых вхождений
  • n, m - два числа, которые трактуются так: «как минимум n раз, но не больше чем m».
Вот примеры первого варианта:

$ echo "tst" | awk "/te{1}st/{print $0}" $ echo "test" | awk "/te{1}st/{print $0}"

Фигурные скобки в шаблонах, поиск точного числа вхождений

В старых версиях awk нужно было использовать ключ командной строки --re-interval для того, чтобы программа распознавала интервалы в регулярных выражениях, но в новых версиях этого делать не нужно.

$ echo "tst" | awk "/te{1,2}st/{print $0}" $ echo "test" | awk "/te{1,2}st/{print $0}" $ echo "teest" | awk "/te{1,2}st/{print $0}" $ echo "teeest" | awk "/te{1,2}st/{print $0}"


Интервал, заданный в фигурных скобках

В данном примере символ «e» должен встретиться в строке 1 или 2 раза, тогда регулярное выражение отреагирует на текст.

Фигурные скобки можно применять и с классами символов. Тут действуют уже знакомые вам принципы:

$ echo "tst" | awk "/t{1,2}st/{print $0}" $ echo "test" | awk "/t{1,2}st/{print $0}" $ echo "teest" | awk "/t{1,2}st/{print $0}" $ echo "teeast" | awk "/t{1,2}st/{print $0}"


Фигурные скобки и классы символов

Шаблон отреагирует на текст в том случае, если в нём один или два раза встретится символ «a» или символ «e».

▍Символ логического «или»

Символ | - вертикальная черта, означает в регулярных выражениях логическое «или». Обрабатывая регулярное выражение, содержащее несколько фрагментов, разделённых таким знаком, движок сочтёт анализируемый текст подходящим в том случае, если он будет соответствовать любому из фрагментов. Вот пример:

$ echo "This is a test" | awk "/test|exam/{print $0}" $ echo "This is an exam" | awk "/test|exam/{print $0}" $ echo "This is something else" | awk "/test|exam/{print $0}"


Логическое «или» в регулярных выражениях

В данном примере регулярное выражение настроено на поиск в тексте слов «test» или «exam». Обратите внимание на то, что между фрагментами шаблона и разделяющим их символом | не должно быть пробелов.

Фрагменты регулярных выражений можно группировать, пользуясь круглыми скобками. Если сгруппировать некую последовательность символов, она будет восприниматься системой как обычный символ. То есть, например, к ней можно будет применить метасимволы повторений. Вот как это выглядит:

$ echo "Like" | awk "/Like(Geeks)?/{print $0}" $ echo "LikeGeeks" | awk "/Like(Geeks)?/{print $0}"


Группировка фрагментов регулярных выражений

В данных примерах слово «Geeks» заключено в круглые скобки, после этой конструкции идёт знак вопроса. Напомним, что вопросительный знак означает «0 или 1 повторение», в результате регулярное выражение отреагирует и на строку «Like», и на строку «LikeGeeks».

Практические примеры

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

▍Подсчёт количества файлов

Напишем bash-скрипт, который подсчитывает файлы, находящиеся в директориях, которые записаны в переменную окружения PATH . Для того, чтобы это сделать, понадобится, для начала, сформировать список путей к директориям. Сделаем это с помощью sed, заменив двоеточия на пробелы:

$ echo $PATH | sed "s/:/ /g"
Команда замены поддерживает регулярные выражения в качестве шаблонов для поиска текста. В данном случае всё предельно просто, ищем мы символ двоеточия, но никто не мешает использовать здесь и что-нибудь другое - всё зависит от конкретной задачи.
Теперь надо пройтись по полученному списку в цикле и выполнить там необходимые для подсчёта количества файлов действия. Общая схема скрипта будет такой:

Mypath=$(echo $PATH | sed "s/:/ /g") for directory in $mypath do done
Теперь напишем полный текст скрипта, воспользовавшись командой ls для получения сведений о количестве файлов в каждой из директорий:

#!/bin/bash mypath=$(echo $PATH | sed "s/:/ /g") count=0 for directory in $mypath do check=$(ls $directory) for item in $check do count=$[ $count + 1 ] done echo "$directory - $count" count=0 done
При запуске скрипта может оказаться, что некоторых директорий из PATH не существует, однако, это не помешает ему посчитать файлы в существующих директориях.


Подсчёт файлов

Главная ценность этого примера заключается в том, что пользуясь тем же подходом, можно решать и куда более сложные задачи. Какие именно - зависит от ваших потребностей.

▍Проверка адресов электронной почты

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

[email protected]
Имя пользователя, username , может состоять из алфавитно-цифровых и некоторых других символов. А именно, это точка, тире, символ подчёркивания, знак «плюс». За именем пользователя следует знак @.

Вооружившись этими знаниями, начнём сборку регулярного выражения с его левой части, которая служит для проверки имени пользователя. Вот что у нас получилось:

^(+)@
Это регулярное выражение можно прочитать так: «В начале строки должен быть как минимум один символ из тех, которые имеются в группе, заданной в квадратных скобках, а после этого должен идти знак @».

Теперь - очередь имени хоста - hostname . Тут применимы те же правила, что и для имени пользователя, поэтому шаблон для него будет выглядеть так:

(+)
Имя домена верхнего уровня подчиняется особым правилам. Тут могут быть лишь алфавитные символы, которых должно быть не меньше двух (например, такие домены обычно содержат код страны), и не больше пяти. Всё это значит, что шаблон для проверки последней части адреса будет таким:

\.({2,5})$
Прочесть его можно так: «Сначала должна быть точка, потом - от 2 до 5 алфавитных символов, а после этого строка заканчивается».

Подготовив шаблоны для отдельных частей регулярного выражения, соберём их вместе:

^(+)@(+)\.({2,5})$
Теперь осталось лишь протестировать то, что получилось:

$ echo "[email protected]" | awk "/^(+)@(+)\.({2,5})$/{print $0}" $ echo "[email protected]" | awk "/^(+)@(+)\.({2,5})$/{print $0}"


Проверка адреса электронной почты с помощью регулярных выражений

То, что переданный awk текст выводится на экран, означает, что система распознала в нём адрес электронной почты.

Итоги

Если регулярное выражение для проверки адресов электронной почты, которое встретилось вам в самом начале статьи, казалось тогда совершенно непонятным, надеемся, сейчас оно уже не выглядит бессмысленным набором символов. Если это действительно так - значит данный материал выполнил своё предназначение. На самом деле, регулярные выражения - это тема, которой можно заниматься всю жизнь, но даже то немногое, что мы разобрали, уже способно помочь вам в написании скриптов, которые довольно продвинуто обрабатывают тексты.

В этой серии материалов мы обычно показывали очень простые примеры bash-скриптов, которые состояли буквально из нескольких строк. В следующий раз рассмотрим кое-что более масштабное.

Уважаемые читатели! А вы пользуетесь регулярными выражениями при обработке текстов в сценариях командной строки?

Одна из наиболее полезных и многофункциональных команд в терминале Linux – команда «grep». Grep – это акроним, который расшифровывается как «global regular expression print» (то есть, «искать везде соответствующие регулярному выражению строки и выводить их»). Это значит, что grep можно использовать для того, чтобы просмотреть, соответствуют ли вводимые данные заданным шаблонам.

Эта на первый взгляд тривиальная программа очень мощна при верном использовании. Ее способность сортировать вводимые данные на основе сложных правил делает ее популярной связкой во многих цепях команд.

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

Основы использования

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

В качестве примера можно использовать grep для поиска строк, содержащих слово «GNU» в версии 3 GNU General Public License на системе Ubuntu.

cd /usr/share/common-licenses
grep "GNU" GPL-3
GNU GENERAL PUBLIC LICENSE





13. Use with the GNU Affero General Public License.
under version 3 of the GNU Affero General Public License into a single
...
...

Первый аргумент, «GNU», является искомым шаблоном, а второй аргумент, «GPL-3», является входным файлом, который нужно найти.

В результате будут выведены все строки, содержащие текстовый шаблон. В некоторых дистрибутивах Linux искомый шаблон будет выделен в выведенных строках.

Общие опции

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

При необходимости игнорировать регистр параметра поиска и искать как прописные, так и строчные вариации шаблона, можно использовать утилиты «-i» или «—ignore-case».

Для примера можно использовать grep для поиска в том же файле слова «license», написанного верхним, нижним или смешанным регистром.

grep -i "license" GPL-3
GNU GENERAL PUBLIC LICENSE
of this license document, but changing it is not allowed.
The GNU General Public License is a free, copyleft license for
The licenses for most software and other practical works are designed
the GNU General Public License is intended to guarantee your freedom to
GNU General Public License for most of our software; it applies also to


"This License" refers to version 3 of the GNU General Public License.
"The Program" refers to any copyrightable work licensed under this
...
...

Как можно видеть, выведенные результаты содержат «LICENSE», «license», and «License». Если бы в файле был экземпляр «LiCeNsE», он также был бы выведен.
При необходимости найти все строки, которые не содержат указанный шаблон, можно использовать флаги «-v» или «—invert-match».

Для примера можно применить следующую команду для поиска в лицензии BSD всех строк, которые не содержат слово «the»:

grep -v "the" BSD
All rights reserved.
Redistribution and use in source and binary forms, with or without
are met:
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS"" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
...
...

Как можно видеть, последние две строки были выведены как не содержащие слова «the», поскольку команда «ignore case» не была использована.

Всегда полезно знать номера строк, в которых были обнаружены совпадения. Их можно узнать при помощи флагов «-n» или «—line-number» .

Если применить данный флаг в предыдущем примере, будет выведен следующий результат:

grep -vn "the" BSD
2:All rights reserved.
3:
4:Redistribution and use in source and binary forms, with or without
6:are met:
13: may be used to endorse or promote products derived from this software
14: without specific prior written permission.
15:
16:THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS"" AND
17:ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
...
...

Теперь можно сослаться на номер строки при необходимости внести изменения в каждой строке, которая не содержит «the».

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

Как было сказано во вступлении, grep расшифровывается как «global regular expression print». Регулярное выражение – это текстовая строка, которая описывает определенный шаблон поиска.

Разные приложения и языки программирования применяют регулярные выражения немного по-разному. В данном руководстве рассматривается только небольшое подмножество способов описания шаблонов для Grep.

Буквенные совпадения

В приведенных выше примерах поиска слов «GNU» и «the» разыскивались очень простые регулярные выражения, точно соответствующие строке символов «GNU» и «the».

Правильнее представлять их именно как совпадения строк символов, чем как совпадения слов. После ознакомления с более сложными шаблонами это разграничение станет более существенным.

Шаблоны, точно соответствующие заданным символам, называются «буквенными», поскольку они соответствуют шаблону побуквенно, символ в символ.

Все буквенные и числовые символы (а также некоторые другие символы) совпадают буквально, если они не были изменены другими механизмами выражения.

Совпадения анкоров

Анкоры – это специальные символы, которые указывают местонахождение в строке необходимого совпадения.

К примеру, можно указать, что при поиске нужны только строки, содержащие слово «GNU» в самом начале. Для этого нужно использовать анкор «^» перед буквенной строкой.

В этом примере выведены только строки, содержащие в самом начале слово «GNU».

grep "^GNU" GPL-3
GNU General Public License for most of our software; it applies also to
GNU General Public License, you may choose any version ever published

Аналогично, анкор «$» можно использовать после буквенной строки, чтобы указать, что совпадение действительно, только если искомая строка символов находится в конце текстовой строки.

В следующем регулярном выражении выведены только те строки, которые содержат «and» в конце:

grep "and$" GPL-3
that there is no warranty for this free software. For both users" and
The precise terms and conditions for copying, distribution and


alternative is allowed only occasionally and noncommercially, and
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
provisionally, unless and until the copyright holder explicitly and
receives a license from the original licensors, to run, modify and
make, use, sell, offer for sale, import and otherwise run, modify and

Совпадение любого символа

Точка (.) используется в регулярных выражениях, чтобы обозначить, что в указанном месте может находиться любой символ.

К примеру, при необходимости найти совпадения, содержащие два символа и затем последовательность «cept», нужно использовать следующий шаблон:

grep "..cept" GPL-3
use, which is precisely where it is most unacceptable. Therefore, we
infringement under applicable copyright law, except executing it on a
tells the user that there is no warranty for the work (except to the

form of a separately written license, or stated as exceptions;
You may not propagate or modify a covered work except as expressly
9. Acceptance Not Required for Having Copies.
...
...

Как можно видеть, в результатах выведены слова «accept» and «except», а также вариации этих слов. Шаблон также совпал бы с последовательностью «z2cept», если бы такая была в тексте.

Выражения в скобках

Поместив группу символов в квадратные скобки («»), можно указать, что в данной позиции может находиться любой из взятых в скобки символов.

Это значит, что при необходимости найти строки, содержащие «too» или «two», можно кратко указать данные вариации, используя следующий шаблон:

grep "to" GPL-3
your programs, too.

Developers that use the GNU GPL protect your rights with two steps:
a computer network, with no transfer of a copy, is not conveying.

Corresponding Source from a network server at no charge.
...
...

Как можно видеть, обе вариации были найдены в файле.

Внесение символов в скобки также предоставляет несколько полезных возможностей. Можно указать, что с шаблоном совпадает все, кроме символов в скобках, если начать список символов, внесенных в скобки, с символа «^».

В данном примере используется шаблон «.ode», с которым не должна совпадать последовательность «code».

grep "[^c]ode" GPL-3
1. Source Code.
model, to give anyone who possesses the object code either (1) a
the only significant mode of use of the product.
notice like this when it starts in an interactive mode:

Стоит заметить, что вторая выведенная строка содержит слово «code». Это не ошибка регулярного выражения или команды grep.

Вернее, эта строка была выведена, потому что она также содержит соответствующую шаблону последовательность «mode», найденную в слове «model». То есть, строка была выведена потому, что в ней было обнаружено совпадение с шаблоном.

Еще одна полезная функция скобок – возможность указать диапазон символов вместо того, чтобы отдельно вводить каждый символ.

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

grep "^" GPL-3
GNU General Public License for most of our software; it applies also to

License. Each licensee is addressed as "you". "Licensees" and


System Libraries, or general-purpose tools or generally available free
Source.

...
...

В связи с некоторыми наследственными проблемами сортировки, для более точного результата лучше использовать классы символов стандарта POSIX вместо диапазона символов, использованного в примере выше.
Существует множество классов символов, не охваченных данным руководством; к примеру, чтобы выполнить ту же процедуру, что и в примере выше, можно использовать класс символов «[:upper:]» в скобках.

grep "^[[:upper:]]" GPL-3
GNU General Public License for most of our software; it applies also to
States should not allow patents to restrict development and use of
License. Each licensee is addressed as "you". "Licensees" and
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
System Libraries, or general-purpose tools or generally available free
Source.
User Product is transferred to the recipient in perpetuity or for a
...
...

Шаблон повторения (0 или больше раз)

Одним из наиболее часто используемых метасимволов является символ «*», что означает «повторить предыдущий символ или выражение 0 или больше раз».

К примеру, при необходимости найти каждую строку с открывающимися или закрывающимися круглыми скобками, что содержат только буквы и одиночные пробелы между ними, можно использовать следующее выражение:

grep "(*)" GPL-3

distribution (with or without modification), making available to the
than the work as a whole, that (a) is included in the normal form of
Component, and (b) serves only to enable use of the work with that
(if any) on which the executable work runs, or a compiler used to
(including a physical distribution medium), accompanied by the
(including a physical distribution medium), accompanied by a
place (gratis or for a charge), and offer equivalent access to the
...
...

Как избежать метасимволов

Иногда может понадобиться искать буквальную точку или буквальную открытую скобку. Поскольку данные символы имеют определенное значение в регулярных выражениях, необходимо «избегать» их, говоря команде grep, что в данном случае использовать их особое значение не нужно.

Этих символов можно избежать, используя обратную косую (\) перед символом, который обычно имеет особое значение.

К примеру, при необходимости найти строку, что начинается с заглавной и заканчивается точкой, можно использовать приведенное ниже выражение. Обратная косая перед последней точкой говорит команде «избегать» ее, так что последняя точка представляет буквальную точку и не имеет значения «любой символ»:

grep "^.*\.$" GPL-3
Source.
License by making exceptions from one or more of its conditions.
License would be to refrain entirely from conveying the Program.
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
SUCH DAMAGES.
Also add information on how to contact you by electronic and paper mail.

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

Команду Grep можно также использовать с расширенным языком регулярных выражений при помощи флага «-E» или же вызывая команду «egrep» вместо «grep».

Эти команды открывают возможности «расширенных регулярных выражений». Расширенные регулярные выражения включают в себя все основные метасимволы, а также дополнительные метасимволы для выражения более сложных совпадений.

Группирование

Одна из простейших и полезнейших возможностей, которые открывают расширенные регулярные выражения, — это возможность группировать выражения и использовать их как единое целое.

Для группирования выражений используются круглые скобки. При необходимости использовать круглые скобки вне расширенных регулярных выражений, их можно «избежать» при помощи обратной косой

grep "\(grouping\)" file.txt
grep -E "(grouping)" file.txt
egrep "(grouping)" file.txt

Приведенные выше выражения являются эквивалентами.

Чередование

Подобно тому, как квадратные скобки задают различные возможные варианты совпадения одного символа, чередование позволяет указать альтернативные совпадения для строк символов или наборов выражений.

Для обозначения чередования используется символ вертикальной черты «|». Чередование часто применяется в группировании для того, чтобы указать, что один из двух или более возможных вариантов должен рассматриваться как совпадение.

В данном примере нужно найти «GPL» или «General Public License»:

grep -E "(GPL|General Public License)" GPL-3
The GNU General Public License is a free, copyleft license for
the GNU General Public License is intended to guarantee your freedom to
GNU General Public License for most of our software; it applies also to
price. Our General Public Licenses are designed to make sure that you
Developers that use the GNU GPL protect your rights with two steps:
For the developers" and authors" protection, the GPL clearly explains
authors" sake, the GPL requires that modified versions be marked as
have designed this version of the GPL to prohibit the practice for those
...
...

Чередование можно использовать для выбора между двумя и более вариантами; для этого нужно ввести остальные варианты в группу отбора, отделяя каждый при помощи символа вертикальной черты «|».

Кванторы

В расширенных регулярных выражениях существуют метасимволы, указывающие частоту повторения символа, подобно тому, как метасимвол «*» указывает на совпадения предыдущего символа или строки символов 0 или более раз.

Чтобы указать совпадение символа 0 или больше раз, можно использовать символ «?». Он сделает предыдущий символ или ряд символов, по сути, необязательными.

В данном примере при помощи внесения последовательности «copy» в факультативную группу выведены совпадения «copyright» и «right»:

grep -E "(copy)?right" GPL-3
Copyright (C) 2007 Free Software Foundation, Inc.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
"Copyright" also means copyright-like laws that apply to other kinds of
...
...

Символ «+» ищет совпадения выражений 1 или больше раз. Он работает почти как символ «*», но при использовании «+» выражение должно совпасть хотя бы 1 раз.

Приведенное ниже выражение ищет совпадения строки «free» плюс 1 или больше символов, которые не являются пробельными:

grep -E "free[^[:space:]]+" GPL-3
The GNU General Public License is a free, copyleft license for
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
When we speak of free software, we are referring to freedom, not
have the freedom to distribute copies of free software (and charge for

freedoms that you received. You must make sure that they, too, receive
protecting users" freedom to change the software. The systematic
of the GPL, as needed to protect the freedom of users.
patents cannot be used to render the program non-free.

Количество повторений совпадений

При необходимости указать количество повторения совпадений можно использовать фигурные скобки («{ }»). Эти символы используются для указания точного количества, диапазона, а также верхнего и нижнего предела количества совпадений выражения.

При необходимости найти все строки, что содержат сочетание трех гласных, можно использовать следующее выражение:

grep -E "{3}" GPL-3
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
receive it, in any medium, provided that you conspicuously and
give under the previous paragraph, plus a right to possession of the
covered work so as to satisfy simultaneously your obligations under this
При необходимости найти все слова, состоящие из 16-20 символов, используйте следующее выражение:
grep -E "[[:alpha:]]{16,20}" GPL-3
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
c) Prohibiting misrepresentation of the origin of that material, or

Выводы

Во многих случаях команда grep бывает полезна для поиска шаблонов внутри файлов или в иерархии файловой системы. Она значительно экономит время, потому стоит ознакомиться с ее параметрами и синтаксисом.

Регулярные выражения еще более многофункциональны и могут быть использованы во многих популярных программах. К примеру, многие текстовые редакторы применяют регулярные выражения для поиска и замены текста.

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

Tags: ,

Одна из наиболее полезных и многофункциональных команд в терминале Linux – команда «grep». Название представляет собой акроним английской фразы «search Globally for lines matching the Regular Expression, and Print them» (искать везде соответствующие регулярному выражению строки и выводить их). Команда «grep» просматривает входной поток последовательно, строка за строкой, в поисках совпадений и выводит (отфильтровывает) только те строки, которые содержат текст, совпадающий с заданным шаблоном – регулярным выражением .

Регулярные выражения - специальный формальный язык поиска и осуществления манипуляций с подстроками в тексте, основанный на использовании метасимволов. Сейчас уже практически все современные языки программирования имеют встроенную поддержку регулярных выражений для обработки текстов, однако исторически популяризации данного подхода во многом способствовал именно мир UNIX и в частности идеи, заложенные в команды «grep», «sed» и др. Философия «все есть файл» полностью пронизывает UNIX и владение инструментами для работы с текстовыми файлами является одним из обязательных навыков каждого пользователя Linux.

ОБРАЗЕЦ

GIST | Простейший поиск всех строк, в которых есть текст «Adams». При оформлении этого и последующих примеров будем придерживаться следующего порядка: сверху параметры командной строки, внизу стандартные потоки слева ввода stdin и справа вывода stdout .

Команда «grep» имеет внушительное количество опций, которые можно указать при запуске. С помощью этих опций можно делать много полезных вещей и при этом в принципе даже не обязательно хорошо разбираться в синтаксисе регулярных выражений.

ОПЦИИ

Начнём с того, что «grep» умеет не только фильтровать стандартный ввод stdin , но и осуществлять поиск по файлам. По умолчанию «grep» будет искать только в файлах, находящихся в текущем каталоге, однако при помощи очень полезной опции --recursive можно сказать команде «grep» искать рекурсивно начиная с заданной директории.

GIST | По умолчанию команда «grep» чувствительна к регистру. Следующий пример показывает как можно искать и при этом не учитывать регистр, например «Adams» и «adams» одно и то же:

Ignore-case "adams"

George Washington, 1789-1797 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 John Adams, 1797-1801

GIST | Поиск наоборот (иногда говорят инвертный поиск), то есть будут выведены все строки, кроме имеющих вхождение указанного шаблона:

Invert-match "Adams"

George Washington, 1789-1797 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 George Washington, 1789-1797 Thomas Jefferson, 1801-1809

GIST | Опции конечно же можно и нужно комбинировать друг с другом. Например поиск наоборот с выводом порядковых номеров строк с вхождениями:

Line-number --invert-match "Adams"

George Washington, 1789-1797 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 1:George Washington, 1789-1797 3:Thomas Jefferson, 1801-1809

GIST | Раскраска. Иногда удобно, когда искомое нами слово подсвечивается цветом. Все это уже есть в «grep», остается только включить:

Line-number --color=always "Adams"

George Washington, 1789-1797 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 2:John Adams, 1797-1801

GIST | Мы хотим выбрать все ошибки из лог файла, но знаем что в следующей сточке после ошибки может содержаться полезная информация, тогда удобно вывести несколько строк из контекста. По умолчанию «grep» выводит лишь строку, в которой было найдено совпадение, но есть несколько опций, позволяющих заставить «grep» выводить больше. Для вывода нескольких строк (в нашем случае двух) после вхождения:

Color=always -A2 "Adams"

George Washington, 1789-1797 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 James Madison, 1809-1817 James Monroe, 1817-1825 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 James Madison, 1809-1817

GIST | Аналогично для дополнительного вывода нескольких строк перед вхождением:

Color=always -B2 "James"

George Washington, 1789-1797 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 James Madison, 1809-1817 James Monroe, 1817-1825 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 James Madison, 1809-1817 James Monroe, 1817-1825

GIST | Однако чаще всего требуется выводить симметричный контекст, для этого есть ещё более сокращённая запись. Выведем по две строки как сверху так и снизу от вхождения:

Color=always -C2 "James"

George Washington, 1789-1797 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 James Madison, 1809-1817 James Monroe, 1817-1825 John Quincy Adams, 1825-1829 Andrew Jackson, 1829-1837 Martin Van Buren, 1837-1841 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 James Madison, 1809-1817 James Monroe, 1817-1825 John Quincy Adams, 1825-1829 Andrew Jackson, 1829-1837

GIST | Когда Вы ищете qwe , то по умолчанию «grep» будет выводить также, qwe123 , 345qwerty и тому подобные комбинации. Найдём только те строки, которые выключают именно всё слово целиком:

Word-regexp --color=always "John"

John Fitzgerald Kennedy, 1961-1963 Lyndon Baines Johnson, 1963-1969 John Fitzgerald Kennedy, 1961-1963

GIST | Ну и напоследок если Вы просто хотите знать количество строк с совпадениями одним единственным числом, но при этом не выводить больше ничего:

Count --color=always "John"

John Fitzgerald Kennedy, 1961-1963 Lyndon Baines Johnson, 1963-1969 Richard Milhous Nixon, 1969-1974 2

Стоит отметить, что у большинства опций есть двойник, например --ignore-case можно привести к более короткому виду -i и т.д.

БАЗОВЫЕ РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ

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

Знак каретки ^ и доллара $ имеют в регулярном выражении особый смысл. Их называют «якорями» (anchor). Якоря – это специальные символы, которые указывают местонахождение в строке необходимого совпадения. Когда поиск доходит до якоря, он проверяет, есть ли соответствие, и если есть – продолжает идти по шаблону, не прибавляя ничего к результату .

GIST | Якорь каретка используют чтобы указать, что регулярное выражение необходимо проверить именно с начала строки:

Color=always "^J"

George Washington, 1789-1797 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 John Adams, 1797-1801

GIST | Аналогично якорь доллар стоит использовать в конце шаблона, чтобы указать, что совпадение действительно только если искомая строка символов находится в конце текстовой строки и никак иначе:

Color=always "9$"

George Washington, 1789-1797 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 Thomas Jefferson, 1801-1809

GIST | Любой символ. Символ точка используется в регулярных выражениях для того, чтобы обозначить, что в указанном месте может находиться абсолютно любой символ:

Color=always "0.$"

GIST | Экранирование. Если нужно найти именно символ точка, тогда экранирование в помощь. Знак экранирования (как правило это обратный слеш), предшествующий символу вроде точки, превращает метасимвол в литерал:

Color=always "\."

George Washington. 1789-1797 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 George Washington. 1789-1797

GIST | Классы символов. В регулярных выражениях можно использовать диапазоны и классы символов. Для этого при составлении шаблона используются квадратные скобки. Поместив группу символов (включая символы, которые в противном случае были бы истолкованы как метасимволы) в квадратные скобки, можно указать, что в данной позиции может находиться любой из взятых в скобки символов:

Color=always "0"

George Washington, 1789-1797 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 John Adams, 1797-1801 Thomas Jefferson, 1801-1809

GIST | Диапазон. Это два символа, разделенные дефисом, например, 0-9 (десятичные цифры) или 0-9a-fA-F (шестнадцатеричные цифры):

Color=always ""

George Washington, ??? John Adams, 1797-1801 Thomas Jefferson, 1801-1809 John Adams, 1797-1801 Thomas Jefferson, 1801-1809

GIST | Отрицание. Если первым символом выражения в квадратных скобках является каретка, то остальные символы принимаются как набор символов, которые не должны присутствовать в заданной позиции регулярного выражения:

Color=always "[^7]$"

George Washington, 1789-1797 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 John Adams, 1797-1801 Thomas Jefferson, 1801-1809

GIST | Классы символов POSIX. Существует некий набор уже заранее заготовленных классов символов, которые Вы можете использовать в регулярных выражениях. Их там с десяток, достаточно быстро просмотреть мануал чтобы понять назначение каждого. Например отфильтруем только шестнадцатеричные цифры:

Color=always "^[[:xdigit:]]*$"

4.2 42 42abc 42 42abc

GIST | Повторение (0 или больше раз). Одним из наиболее часто используемых метасимволов является символ звёздочка, что означает «повторить предыдущий символ или выражение ноль или больше раз»:

Color=always "^*$"

George Washington, ??? John Adams, 1797-1801 Thomas Jefferson, 1801-1809 George Washington, ???

Различают базовые регулярные выражения BRE (basic regular expressions) и расширенные ERE (extended regular expressions). В BRE распознаются следующие метасимволы ^ $ . * и все другие символы расцениваются как литералы. В ERE добавлены ещё такие метасимволы () { } ? + | и связанные с ними функции. Ну а чтобы всех окончательно запутать в «grep» придумали такую штуку – символы () { } в BRE обрабатываются как метасимволы, если они экранированы обратным слешем, в то время как в ERE постановка перед любыми метасимволами обратного слеша приводит к тому, что они трактуются как литералы.

РАСШИРЕННЫЕ РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ

GIST | Дизъюнкция. Подобно тому, как квадратные скобки задают различные возможные варианты совпадения одного символа, дизъюнкция позволяет указать альтернативные совпадения для строк символов или выражений. Для обозначения дизъюнкции используется символ вертикальной черты:

Extended-regexp --color=always "George|John"

George Washington, 1789-1797 John Adams, 1797-1801 Thomas Jefferson, 1801-1809 George Washington, 1789-1797 John Adams, 1797-1801

GIST | Совпадение ноль или один раз. В расширенных регулярных выражениях существуют несколько дополнительных метасимволов, указывающих частоту повторения символа или выражения (подобно тому, как метасимвол звездочка указывает на совпадения 0 или более раз). Один из таких метасимволов это вопросительный знак, который делает предыдущий символ или выражение, по сути, необязательными:

Extended-regexp --color=always "^(Andrew)?John"

John Adams, 1797-1801 Andrew Johnson, 1865-1869 Lyndon Baines Johnson, 1963-1969 John Adams, 1797-1801 Andrew Johnson, 1865-1869

GIST | Совпадение один или более раз. Для этого предусмотрен метасимвол в виде знака плюс. Он работает почти как символ звездочка, за исключением того, что выражение должно совпасть хотя бы один раз:

Extended-regexp --color=always "^[[:alpha:] ]+$"

John Adams Andrew Johnson, 1865-1869 Lyndon Baines Johnson, 1963-1969 John Adams

GIST | Совпадение указанное количество раз. Для этого можно использовать фигурные скобки. Эти метасимволы используются для указания точного количества, диапазона, а также верхнего и нижнего предела количества совпадений выражения:

Extended-regexp --color=always "{1,3}\.{1,3}\.{1,3}\.{1,3}"

42 127.0.0.1 127.0.0.1

Команда grep настолько полезна, многофункциональна и проста в употреблении, что, однажды познакомившись с ней, невозможно представить себе работу без нее.

Предыстория и источник: не все, кому приходится использовать регулярные выражения, до конца понимают, как они работают и как их создавать. Я тоже относился к этой группе - искал примеры регулярных выражений, подходящих под мои задачи, пытался их подправить по мере необходимости. Для меня всё в корне изменилось после прочтения книги The Linux Command Line (Second Internet Edition) автора William E. Shotts, Jr. В ней принципы работы регулярных выражений изложены настолько ясно, что после прочтения я научился их понимать, создавать регулярные выражения любой сложности и теперь использую их при каждой необходимости. Данный материал представляет собой перевод части главы, посвящённой регулярным выражениям. Этот материал предназначен для абсолютных новичков, которые совершенно не понимают, как работают регулярные выражения, но имеют некоторые представления о работе . Надеюсь, эта статья поможет вам сделать такой же прорыв, который помогла мне. Если изложенный здесь материал не содержит ничего нового для вас, попробуйте посмотреть статью «Регулярные выражения и команда grep », в ней более подробно описываются опции grep, а также имеются дополнительные примеры.

Как используются регулярные выражения

Текстовые данные играют важную роль во всех Unix-подобных системах, таких как Linux. Среди прочего, текстом является и вывод консольных программ, и файлы конфигурации, отчётов и т.д. Регулярные выражения являются (пожалуй) одной из самых сложных концепций по работе с текстом, поскольку предполагают высокий уровень абстракции. Но время, потраченное на их изучение, с лихвой окупится. Умея использовать регулярные выражения, вы сможете делать удивительные вещи, хотя их полная ценность может быть не сразу очевидной.

В этой статье будет рассмотрено использование регулярных выражений вместе с командой grep . Но их применение не ограничивается только этим: регулярные выражения поддерживаются другими командами Linux, многими языками программирования, применяются при конфигурации (например, в настройках правил mod_rewrite в Apache), а также некоторые программы с графическим интерфейсом позволяют устанавливать правила для поиска/копирования/удаления с поддержкой регулярных выражений. Даже в популярной офисной программе Microsoft Word для поиска и замены текста вы можете использовать регулярные выражения и подстановочные символы.

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

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

grep

Основной программой, которую мы будем использовать для регулярных выражений, является наш старый приятель, . Имя «grep» на самом деле происходит от фразы «global regular expression print», поэтому мы можем видеть, что grep имеет какое-то отношение к регулярным выражениям. По сути, grep ищет в текстовых файлах текст, который подходит под указанное регулярное выражение и выводит в стандартный вывод любую строку, содержащую соответствие.

grep может делать поиск по тексту, получаемому в стандартном вводе, например:

Ls /usr/bin | grep zip

Эта команда выведет список файлов в директории /usr/bin, чьи имена содержат подстроку «zip».

Программа grep может искать по тексту в файлах.

Общий синтаксис использования:

Grep [опции] regex [файл...]

  • regex - это регулярное выражение.
  • [файл…] - один или несколько файлов, в которых будет проводиться поиск по регулярному выражению.

[опции] и [файл…] могут отсутствовать.

Список самых часто используемых опций grep:

Опция Описание
-i Игнорировать регистр. Не делать различия между большими и маленькими символами. Также можно задать опцией --ignore-case .
-v Инвертировать соответствие. Обычно grep печатает строки, которые содержат соответствие. Эта опция приводит к тому, что grep выводит каждую строку, которая не содержит соответствия. Также можно использовать --invert-match .
-c Печатать количество соответствий (или несоответствий, если указана опция -v ) вместо самих строк. Можно также указывать опцией --count .
-l Вместо самих строк печатать имя каждого файла, который содержит соответствие. Можно указать опцией --files-with-matches .
-L Как опция -l , но печатает только имена файлов, которые не содержат совпадений. Другое имя опции --files-withoutmatch .
-n Добавление к началу каждой совпавшей строке номера строчки внутри файла. Другое имя опции --line-number .
-h Для поиска по нескольким файлам, подавлять вывод имени файла. Также можно указать опцией --no-filename .

Чтобы полнее исследовать grep, давайте создадим несколько текстовых файлов для поиска:

Ls /bin > dirlist-bin.txt ls /usr/bin > dirlist-usr-bin.txt ls /sbin > dirlist-sbin.txt ls /usr/sbin > dirlist-usr-sbin.txt ls dirlist*.txt dirlist-bin.txt dirlist-sbin.txt dirlist-usr-bin.txt dirlist-usr-sbin.txt

Мы можем выполнить простой поиск по нашему списку файлов следующим образом:

Grep bzip dirlist*.txt dirlist-bin.txt:bzip2 dirlist-bin.txt:bzip2recover

В этом примере grep ищет по всем перечисленным файлам строку bzip и находит два соответствия, оба в файле dirlist-bin.txt. Если нас интересует только список файлов, содержащих соответствия, а не сами подходящие строки, мы можем указать опцию -l :

Grep -l bzip dirlist*.txt dirlist-bin.txt

И наоборот, если бы мы хотели увидеть только список файлов, которые не содержали совпадений, мы могли бы сделать это:

Grep -L bzip dirlist*.txt dirlist-sbin.txt dirlist-usr-bin.txt dirlist-usr-sbin.txt

Если вывод отсутствует - значит файлы, удовлетворяющие условиям не найдены.

Метасимволы и литералы

Хотя это может показаться неочевидным, наши поиски с grep всегда используют регулярные выражения, хоть и очень простые. Регулярное выражение «bzip» означает, что совпадение будет происходить (т.е. строка будет считаться подходящей) только в том случае, если строка в файле содержит не менее четырех символов и что где-то в строке символы «b», «z», «i» и «p» находятся в этом порядке, без других символов между ними. Символы в строке «bzip» являются литералами , т.е. буквальными символами , поскольку они соответствуют самим себе. В дополнение к литералам регулярные выражения могут также включать метасимволы , которые используются для задания более сложных совпадений. Метасимволы регулярного выражения состоят из следующих:

^ $ . { } - ? * + () | \

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

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

Любой символ

Первый метасимвол, с которого мы начнём знакомство, это символ точки , который означает «любой символ». Если мы включим его в регулярное выражение, то он будет соответствовать любому символу для этой позиции символа. Пример:

Grep -h ".zip" dirlist*.txt bunzip2 bzip2 bzip2recover gunzip gzip funzip gpg-zip mzip p7zip preunzip prezip prezip-bin unzip unzipsfx

Мы искали любую строку в наших файлах, которая соответствует регулярному выражению «.zip». Нужно отметить парочку интересных моментов в полученных результатах. Обратите внимание, что программа zip не была найдена. Это от того, что включение метасимвола точка в наше регулярное выражение увеличило длину, требуемую для совпадения, до четырёх символов, а поскольку имя «zip» содержит только три, то оно не соответствует. Также если какие-либо файлы из наших списков содержали расширение файла.zip, они также считались бы подходящими, поскольку символ точки в расширении файла, также подходит под условие «любой символ».

Анкоры

Символ каретки (^ ) и знак доллара ($ ) считаются в регулярных выражениях анкорами . Это означает, что они вызывают совпадение, только если регулярное выражение найдено в начале строки (^ ) или в конце строки ($ ):

Grep -h "^zip" dirlist*.txt zip zipcloak zipdetails zipgrep zipinfo zipnote zipsplit grep -h "zip$" dirlist*.txt gunzip gzip funzip gpg-zip mzip p7zip preunzip prezip unzip zip grep -h "^zip$" dirlist*.txt zip

Здесь мы искали по спискам файлов строку «zip», расположенную в начале строки, в конце строки, а также в строке, где она была бы одновременно и в начале, и в конце (т.е. вся строка содержала бы только «zip»). Обратите внимание, что регулярное выражение «^$ » (начало и конец между которыми ничего нет) будет соответствовать пустым строкам.

Небольшое лирическое отступление: помощник по разгадыванию кроссвордов

Даже с нашими ограниченными на данный момент познаниями в регулярных выражениях мы можем делать что-то полезное.

Если вы когда-нибудь разгадывали кроссворды, то вам нужно было решать задачи вроде «что за слово из пяти букв, где третья буква «j», а последняя буква «r», которое означает…». Этот вопрос может заставить задуматься. Знаете ли вы, что в системе Linux есть словарь? А он есть. Загляните в директорию /usr/share/dict, там вы можете найти один или несколько словарей. Словари, размещённые там, это просто длинные списки слов по одному на строку, расположенные в алфавитном порядке. В моей системе файл словаря содержит 99171 слов. Для поиска возможных ответов на вышеприведённый вопрос кроссворда, мы можем сделать так:

Grep -i "^..j.r$" /usr/share/dict/american-english Major major

Используя это регулярное выражение, мы можем найти все слова в нашем файле словаря, которое имеет длину в пять букв, имеет «j» в третьей позиции и «r» в последней позиции.

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

Выражения в квадратных скобках и Классы символов

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

Grep -h "zip" dirlist*.txt bzip2 bzip2recover gzip

мы найдём любые строчки, содержащие строки «bzip» или «gzip».

Набор может содержать любое количество символов, а метасимволы теряют своё специальное значение, когда помещаются внутрь квадратных скобок. Тем не менее, есть два случая в которых метасимволы, используемые внутри квадратных скобок, имеют различные значения. Первый - это каретка (^ ), которая используется для указания отрицания; второй - это тире (- ), которое используется для указания диапазона символов.

Отрицание

Если первым символом выражения в квадратных скобках является каретка (^ ), то остальные символы принимаются как набор символов, которые не должны присутствовать в заданной позиции символа. Сделаем это изменив наш предыдущий пример:

Grep -h "[^bg]zip" dirlist*.txt bunzip2 gunzip funzip gpg-zip mzip p7zip preunzip prezip prezip-bin unzip unzipsfx

С активированным отрицанием, мы получили список файлов, которые содержат строку «zip», перед которой идёт любой символ, кроме «b» или «g». Обратите внимание, что zip не был найден. Отрицаемый набор символов всё равно требует символ на заданной позиции, но символ не должен быть членом инвертированного набора.

Символ каретки вызывает отрицание только если он является первым символом внутри выражения в квадратных скобках; в противном случае, он теряет своё специальное назначение и становится обычным символом из набора.

Традиционные диапазоны символов

Если мы хотим сконструировать регулярное выражение, которое должно найти каждый файл из нашего списка, начинающийся на заглавную букву, мы можем сделать следующее:

Grep -h "^" dirlist*.txt MAKEDEV GET HEAD POST VBoxClient X X11 Xorg ModemManager NetworkManager VBoxControl VBoxService

Суть в том, что мы разместили все 26 заглавных букв в выражение внутри квадратных скобок. Но мысль печатать их все не вызывает энтузиазма, поэтому есть другой путь:

Grep -h "^" dirlist*.txt

Используя трёхсимвольный диапазон, мы можем сократить запись из 26 букв. Таким способом можно выразить любой диапазон символов, включая сразу несколько диапазонов, такие, как это выражение, которое соответствует всем именам файлов, начинающихся с букв и цифр:

Grep -h "^" dirlist*.txt

В диапазонах символов мы видим, что символ чёрточки трактуется особым образом, поэтому как мы можем включить символ тире в выражение внутри квадратных скобок? Сделав его первым символом в выражении. Рассмотрим два примера:

Grep -h "" dirlist*.txt

Это будет соответствовать каждому имени файла, содержащему заглавную букву. При этом:

Grep -h "[-AZ]" dirlist*.txt

будет соответствовать каждому имени файла, содержащему тире, или заглавную «A», или заглавную «Z».

Для того, чтобы полноценно обрабатывать тексты в bash-скриптах с помощью sed и awk, просто необходимо разобраться с регулярными выражениями. Реализации этого полезнейшего инструмента можно найти буквально повсюду, и хотя устроены все регулярные выражения схожим образом, основаны на одних и тех же идеях, в разных средах работа с ними имеет определённые особенности. Тут мы поговорим о регулярных выражениях, которые подходят для использования в сценариях командной строки Linux.

Этот материал задуман как введение в регулярные выражения, рассчитанное на тех, кто может совершенно не знать о том, что это такое. Поэтому начнём с самого начала.

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

У многих, когда они впервые видят регулярные выражения, сразу же возникает мысль, что перед ними бессмысленное нагромождение символов. Но это, конечно, далеко не так. Взгляните, например, на это регулярное выражение


На наш взгляд даже абсолютный новичок сходу поймёт, как оно устроено и зачем нужно:) Если же вам не вполне понятно - просто читайте дальше и всё встанет на свои места.
Регулярное выражение - это шаблон, пользуясь которым программы вроде sed или awk фильтруют тексты. В шаблонах используются обычные ASCII-символы, представляющие сами себя, и так называемые метасимволы, которые играют особую роль, например, позволяя ссылаться на некие группы символов.

Типы регулярных выражений

Реализации регулярных выражений в различных средах, например, в языках программирования вроде Java, Perl и Python, в инструментах Linux вроде sed, awk и grep, имеют определённые особенности. Эти особенности зависят от так называемых движков обработки регулярных выражений, которые занимаются интерпретацией шаблонов.
В Linux имеется два движка регулярных выражений:
  • Движок, поддерживающий стандарт POSIX Basic Regular Expression (BRE).
  • Движок, поддерживающий стандарт POSIX Extended Regular Expression (ERE).
Большинство утилит Linux соответствуют, как минимум, стандарту POSIX BRE, но некоторые утилиты (в их числе - sed) понимают лишь некое подмножество стандарта BRE. Одна из причин такого ограничения - стремление сделать такие утилиты как можно более быстрыми в деле обработки текстов.

Стандарт POSIX ERE часто реализуют в языках программирования. Он позволяет пользоваться большим количеством средств при разработке регулярных выражений. Например, это могут быть специальные последовательности символов для часто используемых шаблонов, вроде поиска в тексте отдельных слов или наборов цифр. Awk поддерживает стандарт ERE.

Существует много способов разработки регулярных выражений, зависящих и от мнения программиста, и от особенностей движка, под который их создают. Непросто писать универсальные регулярные выражения, которые сможет понять любой движок. Поэтому мы сосредоточимся на наиболее часто используемых регулярных выражениях и рассмотрим особенности их реализации для sed и awk.

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

Пожалуй, самый простой шаблон BRE представляет собой регулярное выражение для поиска точного вхождения последовательности символов в тексте. Вот как выглядит поиск строки в sed и awk:

$ echo "This is a test" | sed -n "/test/p" $ echo "This is a test" | awk "/test/{print $0}"

Поиск текста по шаблону в sed


Поиск текста по шаблону в awk

Можно заметить, что поиск заданного шаблона выполняется без учёта точного места нахождения текста в строке. Кроме того, не имеет значение и количество вхождений. После того, как регулярное выражение найдёт заданный текст в любом месте строки, строка считается подходящей и передаётся для дальнейшей обработки.

Работая с регулярными выражениями нужно учитывать то, что они чувствительны к регистру символов:

$ echo "This is a test" | awk "/Test/{print $0}" $ echo "This is a test" | awk "/test/{print $0}"

Регулярные выражения чувствительны к регистру

Первое регулярное выражение совпадений не нашло, так как слово «test», начинающееся с заглавной буквы, в тексте не встречается. Второе же, настроенное на поиск слова, написанного прописными буквами, обнаружило в потоке подходящую строку.

В регулярных выражениях можно использовать не только буквы, но и пробелы, и цифры:

$ echo "This is a test 2 again" | awk "/test 2/{print $0}"

Поиск фрагмента текста, содержащего пробелы и цифры

Пробелы воспринимаются движком регулярных выражений как обычные символы.

Специальные символы

При использовании различных символов в регулярных выражениях надо учитывать некоторые особенности. Так, существуют некоторые специальные символы, или метасимволы, использование которых в шаблоне требует особого подхода. Вот они:

.*^${}\+?|()
Если один из них нужен в шаблоне, его нужно будет экранировать с помощью обратной косой черты (обратного слэша) - \ .

Например, если в тексте нужно найти знак доллара, его надо включить в шаблон, предварив символом экранирования. Скажем, имеется файл myfile с таким текстом:

There is 10$ on my pocket
Знак доллара можно обнаружить с помощью такого шаблона:

$ awk "/\$/{print $0}" myfile

Использование в шаблоне специального символа

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

$ echo "\ is a special character" | awk "/\\/{print $0}"

Экранирование обратного слэша

Хотя прямой слэш и не входит в приведённый выше список специальных символов, попытка воспользоваться им в регулярном выражении, написанном для sed или awk, приведёт к ошибке:

$ echo "3 / 2" | awk "///{print $0}"

Неправильное использование прямого слэша в шаблоне

Если он нужен, его тоже надо экранировать:

$ echo "3 / 2" | awk "/\//{print $0}"

Экранирование прямого слэша

Якорные символы

Существуют два специальных символа для привязки шаблона к началу или к концу текстовой строки. Символ «крышка» - ^ позволяет описывать последовательности символов, которые находятся в начале текстовых строк. Если искомый шаблон окажется в другом месте строки, регулярное выражение на него не отреагирует. Выглядит использование этого символа так:

$ echo "welcome to likegeeks website" | awk "/^likegeeks/{print $0}" $ echo "likegeeks website" | awk "/^likegeeks/{print $0}"

Поиск шаблона в начале строки

Символ ^ предназначен для поиска шаблона в начале строки, при этом регистр символов так же учитывается. Посмотрим, как это отразится на обработке текстового файла:

$ awk "/^this/{print $0}" myfile


Поиск шаблона в начале строки в тексте из файла

При использовании sed, если поместить крышку где-нибудь внутри шаблона, она будет восприниматься как любой другой обычный символ:

$ echo "This ^ is a test" | sed -n "/s ^/p"

Крышка, находящаяся не в начале шаблона в sed

В awk, при использовании такого же шаблона, данный символ надо экранировать:

$ echo "This ^ is a test" | awk "/s \^/{print $0}"

Крышка, находящаяся не в начале шаблона в awk

С поиском фрагментов текста, находящихся в начале строки мы разобрались. Что, если надо найти нечто, расположенное в конце строки?

В этом нам поможет знак доллара - $ , являющийся якорным символом конца строки:

$ echo "This is a test" | awk "/test$/{print $0}"

Поиск текста, находящегося в конце строки

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

$ awk "/^this is a test$/{print $0}" myfile


Шаблон, в котором использованы специальные символы начала и конца строки

Как видно, шаблон среагировал лишь на строку, полностью соответствующую заданной последовательности символов и их расположению.

Вот как, пользуясь якорными символами, отфильтровать пустые строки:

$ awk "!/^$/{print $0}" myfile
В данном шаблоне использовал символ отрицания, восклицательный знак - ! . Благодаря использованию такого шаблона выполняется поиск строк, не содержащих ничего между началом и концом строки, а благодаря восклицательному знаку на печать выводятся лишь строки, которые не соответствуют этому шаблону.

Символ «точка»

Точка используется для поиска любого одиночного символа, за исключением символа перевода строки. Передадим такому регулярному выражению файл myfile , содержимое которого приведено ниже:

$ awk "/.st/{print $0}" myfile


Использование точки в регулярных выражениях

Как видно по выведенным данным, шаблону соответствуют лишь первые две строки из файла, так как они содержат последовательность символов «st», предварённую ещё одним символом, в то время как третья строка подходящей последовательности не содержит, а в четвёртой она есть, но находится в самом начале строки.

Классы символов

Точка соответствует любому одиночному символу, но что если нужно более гибко ограничить набор искомых символов? В подобной ситуации можно воспользоваться классами символов.

Благодаря такому подходу можно организовать поиск любого символа из заданного набора. Для описания класса символов используются квадратные скобки - :

$ awk "/th/{print $0}" myfile


Описание класса символов в регулярном выражении

Тут мы ищем последовательность символов «th», перед которой есть символ «o» или символ «i».

Классы оказываются очень кстати, если выполняется поиск слов, которые могут начинаться как с прописной, так и со строчной буквы:

$ echo "this is a test" | awk "/his is a test/{print $0}" $ echo "This is a test" | awk "/his is a test/{print $0}"

Поиск слов, которые могут начинаться со строчной или прописной буквы

Классы символов не ограничены буквами. Тут можно использовать и другие символы. Нельзя заранее сказать, в какой ситуации понадобятся классы - всё зависит от решаемой задачи.

Отрицание классов символов

Классы символов можно использовать и для решения задачи, обратной описанной выше. А именно, вместо поиска символов, входящих в класс, можно организовать поиск всего, что в класс не входит. Для того, чтобы добиться такого поведения регулярного выражения, перед списком символов класса нужно поместить знак ^ . Выглядит это так:

$ awk "/[^oi]th/{print $0}" myfile


Поиск символов, не входящих в класс

В данном случае будут найдены последовательности символов «th», перед которыми нет ни «o», ни «i».

Диапазоны символов

В символьных классах можно описывать диапазоны символов, используя тире:

$ awk "/st/{print $0}" myfile


Описание диапазона символов в символьном классе

В данном примере регулярное выражение реагирует на последовательность символов «st», перед которой находится любой символ, расположенный, в алфавитном порядке, между символами «e» и «p».

Диапазоны можно создавать и из чисел:

$ echo "123" | awk "//" $ echo "12a" | awk "//"

Регулярное выражение для поиска трёх любых чисел

В класс символов могут входить несколько диапазонов:

$ awk "/st/{print $0}" myfile


Класс символов, состоящий из нескольких диапазонов

Данное регулярное выражение найдёт все последовательности «st», перед которыми есть символы из диапазонов a-f и m-z .

Специальные классы символов

В BRE имеются специальные классы символов, которые можно использовать при написании регулярных выражений:
  • [[:alpha:]] - соответствует любому алфавитному символу, записанному в верхнем или нижнем регистре.
  • [[:alnum:]] - соответствует любому алфавитно-цифровому символу, а именно - символам в диапазонах 0-9 , A-Z , a-z .
  • [[:blank:]] - соответствует пробелу и знаку табуляции.
  • [[:digit:]] - любой цифровой символ от 0 до 9 .
  • [[:upper:]] - алфавитные символы в верхнем регистре - A-Z .
  • [[:lower:]] - алфавитные символы в нижнем регистре - a-z .
  • [[:print:]] - соответствует любому печатаемому символу.
  • [[:punct:]] - соответствует знакам препинания.
  • [[:space:]] - пробельные символы, в частности - пробел, знак табуляции, символы NL , FF , VT , CR .
Использовать специальные классы в шаблонах можно так:

$ echo "abc" | awk "/[[:alpha:]]/{print $0}" $ echo "abc" | awk "/[[:digit:]]/{print $0}" $ echo "abc123" | awk "/[[:digit:]]/{print $0}"


Специальные классы символов в регулярных выражениях

Символ «звёздочка»

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

$ echo "test" | awk "/tes*t/{print $0}" $ echo "tessst" | awk "/tes*t/{print $0}"


Использование символа * в регулярных выражениях

Этот шаблонный символ обычно используют для работы со словами, в которых постоянно встречаются опечатки, или для слов, допускающих разные варианты корректного написания:

$ echo "I like green color" | awk "/colou*r/{print $0}" $ echo "I like green colour " | awk "/colou*r/{print $0}"

Поиск слова, имеющего разные варианты написания

В этом примере одно и то же регулярное выражение реагирует и на слово «color», и на слово «colour». Это так благодаря тому, что символ «u», после которого стоит звёздочка, может либо отсутствовать, либо встречаться несколько раз подряд.

Ещё одна полезная возможность, вытекающая из особенностей символа звёздочки, заключается в комбинировании его с точкой. Такая комбинация позволяет регулярному выражению реагировать на любое количество любых символов:

$ awk "/this.*test/{print $0}" myfile


Шаблон, реагирующий на любое количество любых символов

В данном случае неважно сколько и каких символов находится между словами «this» и «test».

Звёздочку можно использовать и с классами символов:

$ echo "st" | awk "/s*t/{print $0}" $ echo "sat" | awk "/s*t/{print $0}" $ echo "set" | awk "/s*t/{print $0}"


Использование звёздочки с классами символов

Во всех трёх примерах регулярное выражение срабатывает, так как звёздочка после класса символов означает, что если будет найдено любое количество символов «a» или «e», а также если их найти не удастся, строка будет соответствовать заданному шаблону.

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

Шаблоны стандарта POSIX ERE, которые поддерживают некоторые утилиты Linux, могут содержать дополнительные символы. Как уже было сказано, awk поддерживает этот стандарт, а вот sed - нет.

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

▍Вопросительный знак

Вопросительный знак указывает на то, что предшествующий символ может встретиться в тексте один раз или не встретиться вовсе. Этот символ - один из метасимволов повторений. Вот несколько примеров:

$ echo "tet" | awk "/tes?t/{print $0}" $ echo "test" | awk "/tes?t/{print $0}" $ echo "tesst" | awk "/tes?t/{print $0}"


Вопросительный знак в регулярных выражениях

Как видно, в третьем случае буква «s» встречается дважды, поэтому на слово «tesst» регулярное выражение не реагирует.

Вопросительный знак можно использовать и с классами символов:

$ echo "tst" | awk "/t?st/{print $0}" $ echo "test" | awk "/t?st/{print $0}" $ echo "tast" | awk "/t?st/{print $0}" $ echo "taest" | awk "/t?st/{print $0}" $ echo "teest" | awk "/t?st/{print $0}"


Вопросительный знак и классы символов

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

▍Символ «плюс»

Символ «плюс» в шаблоне указывает на то, что регулярное выражение обнаружит искомое в том случае, если предшествующий символ встретится в тексте один или более раз. При этом на отсутствие символа такая конструкция реагировать не будет:

$ echo "test" | awk "/te+st/{print $0}" $ echo "teest" | awk "/te+st/{print $0}" $ echo "tst" | awk "/te+st/{print $0}"


Символ «плюс» в регулярных выражениях

В данном примере, если символа «e» в слове нет, движок регулярных выражений не найдёт в тексте соответствий шаблону. Символ «плюс» работает и с классами символов - этим он похож на звёздочку и вопросительный знак:

$ echo "tst" | awk "/t+st/{print $0}" $ echo "test" | awk "/t+st/{print $0}" $ echo "teast" | awk "/t+st/{print $0}" $ echo "teeast" | awk "/t+st/{print $0}"


Знак «плюс» и классы символов

В данном случае если в строке имеется любой символ из класса, текст будет сочтён соответствующим шаблону.

▍Фигурные скобки

Фигурные скобки, которыми можно пользоваться в ERE-шаблонах, похожи на символы, рассмотренные выше, но они позволяют точнее задавать необходимое число вхождений предшествующего им символа. Указывать ограничение можно в двух форматах:
  • n - число, задающее точное число искомых вхождений
  • n, m - два числа, которые трактуются так: «как минимум n раз, но не больше чем m».
Вот примеры первого варианта:

$ echo "tst" | awk "/te{1}st/{print $0}" $ echo "test" | awk "/te{1}st/{print $0}"

Фигурные скобки в шаблонах, поиск точного числа вхождений

В старых версиях awk нужно было использовать ключ командной строки --re-interval для того, чтобы программа распознавала интервалы в регулярных выражениях, но в новых версиях этого делать не нужно.

$ echo "tst" | awk "/te{1,2}st/{print $0}" $ echo "test" | awk "/te{1,2}st/{print $0}" $ echo "teest" | awk "/te{1,2}st/{print $0}" $ echo "teeest" | awk "/te{1,2}st/{print $0}"


Интервал, заданный в фигурных скобках

В данном примере символ «e» должен встретиться в строке 1 или 2 раза, тогда регулярное выражение отреагирует на текст.

Фигурные скобки можно применять и с классами символов. Тут действуют уже знакомые вам принципы:

$ echo "tst" | awk "/t{1,2}st/{print $0}" $ echo "test" | awk "/t{1,2}st/{print $0}" $ echo "teest" | awk "/t{1,2}st/{print $0}" $ echo "teeast" | awk "/t{1,2}st/{print $0}"


Фигурные скобки и классы символов

Шаблон отреагирует на текст в том случае, если в нём один или два раза встретится символ «a» или символ «e».

▍Символ логического «или»

Символ | - вертикальная черта, означает в регулярных выражениях логическое «или». Обрабатывая регулярное выражение, содержащее несколько фрагментов, разделённых таким знаком, движок сочтёт анализируемый текст подходящим в том случае, если он будет соответствовать любому из фрагментов. Вот пример:

$ echo "This is a test" | awk "/test|exam/{print $0}" $ echo "This is an exam" | awk "/test|exam/{print $0}" $ echo "This is something else" | awk "/test|exam/{print $0}"


Логическое «или» в регулярных выражениях

В данном примере регулярное выражение настроено на поиск в тексте слов «test» или «exam». Обратите внимание на то, что между фрагментами шаблона и разделяющим их символом | не должно быть пробелов.

Фрагменты регулярных выражений можно группировать, пользуясь круглыми скобками. Если сгруппировать некую последовательность символов, она будет восприниматься системой как обычный символ. То есть, например, к ней можно будет применить метасимволы повторений. Вот как это выглядит:

$ echo "Like" | awk "/Like(Geeks)?/{print $0}" $ echo "LikeGeeks" | awk "/Like(Geeks)?/{print $0}"


Группировка фрагментов регулярных выражений

В данных примерах слово «Geeks» заключено в круглые скобки, после этой конструкции идёт знак вопроса. Напомним, что вопросительный знак означает «0 или 1 повторение», в результате регулярное выражение отреагирует и на строку «Like», и на строку «LikeGeeks».

Практические примеры

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

▍Подсчёт количества файлов

Напишем bash-скрипт, который подсчитывает файлы, находящиеся в директориях, которые записаны в переменную окружения PATH . Для того, чтобы это сделать, понадобится, для начала, сформировать список путей к директориям. Сделаем это с помощью sed, заменив двоеточия на пробелы:

$ echo $PATH | sed "s/:/ /g"
Команда замены поддерживает регулярные выражения в качестве шаблонов для поиска текста. В данном случае всё предельно просто, ищем мы символ двоеточия, но никто не мешает использовать здесь и что-нибудь другое - всё зависит от конкретной задачи.
Теперь надо пройтись по полученному списку в цикле и выполнить там необходимые для подсчёта количества файлов действия. Общая схема скрипта будет такой:

Mypath=$(echo $PATH | sed "s/:/ /g") for directory in $mypath do done
Теперь напишем полный текст скрипта, воспользовавшись командой ls для получения сведений о количестве файлов в каждой из директорий:

#!/bin/bash mypath=$(echo $PATH | sed "s/:/ /g") count=0 for directory in $mypath do check=$(ls $directory) for item in $check do count=$[ $count + 1 ] done echo "$directory - $count" count=0 done
При запуске скрипта может оказаться, что некоторых директорий из PATH не существует, однако, это не помешает ему посчитать файлы в существующих директориях.


Подсчёт файлов

Главная ценность этого примера заключается в том, что пользуясь тем же подходом, можно решать и куда более сложные задачи. Какие именно - зависит от ваших потребностей.

▍Проверка адресов электронной почты

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

[email protected]
Имя пользователя, username , может состоять из алфавитно-цифровых и некоторых других символов. А именно, это точка, тире, символ подчёркивания, знак «плюс». За именем пользователя следует знак @.

Вооружившись этими знаниями, начнём сборку регулярного выражения с его левой части, которая служит для проверки имени пользователя. Вот что у нас получилось:

^(+)@
Это регулярное выражение можно прочитать так: «В начале строки должен быть как минимум один символ из тех, которые имеются в группе, заданной в квадратных скобках, а после этого должен идти знак @».

Теперь - очередь имени хоста - hostname . Тут применимы те же правила, что и для имени пользователя, поэтому шаблон для него будет выглядеть так:

(+)
Имя домена верхнего уровня подчиняется особым правилам. Тут могут быть лишь алфавитные символы, которых должно быть не меньше двух (например, такие домены обычно содержат код страны), и не больше пяти. Всё это значит, что шаблон для проверки последней части адреса будет таким:

\.({2,5})$
Прочесть его можно так: «Сначала должна быть точка, потом - от 2 до 5 алфавитных символов, а после этого строка заканчивается».

Подготовив шаблоны для отдельных частей регулярного выражения, соберём их вместе:

^(+)@(+)\.({2,5})$
Теперь осталось лишь протестировать то, что получилось:

$ echo "[email protected]" | awk "/^(+)@(+)\.({2,5})$/{print $0}" $ echo "[email protected]" | awk "/^(+)@(+)\.({2,5})$/{print $0}"


Проверка адреса электронной почты с помощью регулярных выражений

То, что переданный awk текст выводится на экран, означает, что система распознала в нём адрес электронной почты.

Итоги

Если регулярное выражение для проверки адресов электронной почты, которое встретилось вам в самом начале статьи, казалось тогда совершенно непонятным, надеемся, сейчас оно уже не выглядит бессмысленным набором символов. Если это действительно так - значит данный материал выполнил своё предназначение. На самом деле, регулярные выражения - это тема, которой можно заниматься всю жизнь, но даже то немногое, что мы разобрали, уже способно помочь вам в написании скриптов, которые довольно продвинуто обрабатывают тексты.

В этой серии материалов мы обычно показывали очень простые примеры bash-скриптов, которые состояли буквально из нескольких строк. В следующий раз рассмотрим кое-что более масштабное.

Уважаемые читатели! А вы пользуетесь регулярными выражениями при обработке текстов в сценариях командной строки?


Top