Поиск по сайту:

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


Хотите знать, что эти странные строки символов делают в Linux? Они дают вам магию командной строки! Мы научим вас, как использовать заклинания регулярных выражений, и повысим ваши навыки работы с командной строкой.

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

Регулярные выражения (регулярные выражения) — это способ найти совпадающие последовательности символов. Они используют буквы и символы для определения шаблона, который ищется в файле или потоке. Есть несколько разных вариантов регулярного выражения. Мы рассмотрим версию, используемую в общих утилитах и командах Linux, таких как grep, команда, которая печатает строки, соответствующие шаблону поиска. Это немного отличается от использования стандартного регулярного выражения в контексте программирования.

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

Чтобы использовать расширенные регулярные выражения с grep, вы должны использовать параметр -E (расширенный). Поскольку это очень быстро утомляет, была создана команда egrep. Команда egrep аналогична комбинации grep -E, просто вам не нужно каждый раз использовать параметр -E.

Если вам удобнее использовать egrep, вы можете это сделать. Однако просто имейте в виду, что это официально устарело. Он по-прежнему присутствует во всех проверенных нами дистрибутивах, но может исчезнуть в будущем.

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

С малого начала

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

Вот содержимое файла:

less geek.txt

Отображается первая часть файла.

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

grep -E 'o' geeks.txt

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

У пары имен были двойные О; мы вводим следующее, чтобы перечислить только те:

grep -E 'oo' geeks.txt

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

Мы увидим больше функциональности с нашими шаблонами поиска по мере продвижения вперед.

Номера строк и другие трюки grep

Если вы хотите, чтобы grep выводил номера строк совпадающих записей, вы можете использовать параметр -n (номер строки). Это уловка grep — она не является частью функциональности регулярных выражений. Однако иногда вам может понадобиться узнать, где в файле находятся совпадающие записи.

Набираем следующее:

grep -E -n 'o' geeks.txt

Еще один удобный прием grep, который вы можете использовать, — это параметр -o (только соответствие). Он отображает только совпадающую последовательность символов, а не окружающий текст. Это может быть полезно, если вам нужно быстро просмотреть список на наличие повторяющихся совпадений в любой из строк.

Для этого набираем следующее:

grep -E -n -o 'o' geeks.txt

Если вы хотите сократить вывод до минимума, вы можете использовать параметр -c (счетчик).

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

grep -E -c 'o' geeks.txt

Оператор чередования

Если вы хотите найти вхождения как двойного «l», так и двойного «o», вы можете использовать символ вертикальной черты (|), который является оператором чередования. Он ищет совпадения для шаблона поиска слева или справа от него.

Набираем следующее:

grep -E -n -o 'll|oo' geeks.txt

Любая строка, содержащая двойную букву «l», «o» или и то, и другое, появляется в результатах.

Чувствительность к регистру

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

am|Am

Это будет соответствовать как «am», так и «Am». Для всего, кроме тривиальных примеров, это быстро приводит к громоздким шаблонам поиска. Простой способ обойти это — использовать параметр -i (игнорировать регистр) с grep.

Для этого набираем следующее:

grep -E 'am' geeks.txt
grep -E -i 'am' geeks.txt

Первая команда выдает три результата с выделенными тремя совпадениями. Вторая команда выдает четыре результата, потому что «Am» в «Amanda» также является совпадением.

Анкеровка

Мы можем сопоставить последовательность «Am» и другими способами. Например, мы можем искать именно этот шаблон или игнорировать регистр и указать, что последовательность должна стоять в начале строки.

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

Мы набираем следующее (обратите внимание, что знак вставки находится внутри одинарных кавычек):

grep -E ‘Am’ geeks.txt

grep -E -i '^am' geeks.txt

Обе эти команды соответствуют «Am».

Теперь давайте найдем строки, содержащие двойную букву «n» в конце строки.

Мы вводим следующее, используя знак доллара ($), обозначающий конец строки:

grep -E -i 'nn' geeks.txt
grep -E -i 'nn$' geeks.txt

Подстановочные знаки

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

Мы вводим следующее для поиска шаблонов, которые начинаются с «T», заканчиваются на «m» и имеют один символ между ними:

grep -E 'T.m' geeks.txt

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

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

grep-E 'J...n' geeks.txt

Строка, содержащая «Джейсон», сопоставляется и отображается.

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

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

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

Однако в регулярных выражениях c*t не соответствует cat, cot, coot и т. д. Скорее, оно переводится как соответствует нулю или более символов 'c', за которыми следует на букву «т». Таким образом, он соответствует «t», «ct», «cct», «ccct» или любому количеству символов «c».

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

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

grep -E 'J.*n ' geeks.txt
grep -E 'J.*n ' geeks.txt

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

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

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

Классы персонажей

Допустим, мы хотим найти все строки, начинающиеся с заглавной буквы N или W.

Если мы используем следующую команду, она соответствует любой строке с последовательностью, начинающейся с заглавной «N» или «W», независимо от того, где она появляется в строке:

grep -E 'N|W' geeks.txt

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

grep -E '^N|W' geeks.txt

Поиск соответствует строкам, которые содержат заглавную букву «W» в любом месте строки. Он также соответствует строке «Больше нет», потому что начинается с заглавной «Н». Якорь начала строки (^) применяется только к заглавной «N».

Мы также могли бы добавить якорь начала строки к заглавной «W», но это вскоре стало бы неэффективным в шаблоне поиска, более сложном, чем в нашем простом примере.

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

Мы можем применить привязку начала строки ко всем элементам в списке в квадратных скобках ([]). (Обратите внимание, что якорь начала строки находится вне скобок).

Мы вводим следующее для поиска любой строки, которая начинается с заглавной «N» или «W»:

grep -E '^[NW]' geeks.txt

Мы также будем использовать эти концепции в следующем наборе команд.

Мы вводим следующее, чтобы найти кого-либо по имени Том или Тим:

grep -E 'T[oi]m' geeks.txt

Если знак вставки (^) является первым символом в квадратных скобках ([]), шаблон поиска ищет любой символ, которого нет в списке.

Например, мы набираем следующее, чтобы найти любое имя, начинающееся с «Т», заканчивающееся на «м» и в котором средняя буква не «о»:

grep -E 'T[^o]m' geeks.txt

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

grep -E 'T[aeiou]m' geeks.txt

Интервальные выражения

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

Число само по себе означает именно это число, но если после него поставить запятую (,), это означает это число или больше. Если вы разделяете два числа запятой (1,2), это означает диапазон чисел от наименьшего к наибольшему.

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

Итак, набираем эту команду:

grep -E 'T[aeiou]{1,2}m' geeks.txt

Это соответствует «Тим», «Том» и «Команда».

Если мы хотим найти последовательность «el», мы набираем это:

grep -E 'el' geeks.txt

Мы добавляем вторую «l» к шаблону поиска, чтобы включить только последовательности, содержащие двойную «l»:

grep -E 'ell' geeks.txt

Это эквивалентно этой команде:

grep -E 'el{2}' geeks.txt

Если мы предоставим диапазон «не менее одного и не более двух» вхождений «l», он будет соответствовать последовательностям «el» и «ell».

Это немного отличается от результатов первой из этих четырех команд, в которых все совпадения были для последовательностей «el», в том числе внутри последовательностей «ell» (и выделена только одна «l»).

Набираем следующее:

grep -E 'el{1,2}' geeks.txt

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

grep -E '[aeiou]{2,}' geeks.txt

Экранирование символов

Допустим, мы хотим найти строки, в которых точка (.) является последним символом. Мы знаем, что знак доллара ($) является якорем в конце строки, поэтому мы можем ввести это:

grep -E '.$' geeks.txt

Однако, как показано ниже, мы не получаем того, что ожидали.

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

Итак, как предотвратить выполнение функции регулярного выражения специальным символом, когда вы просто хотите найти этот фактический символ? Для этого вы используете обратную косую черту (\), чтобы экранировать символ.

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

Набираем следующее:

grep -e '\.$' geeks.txt

Это соответствует фактическому символу точки (.) в конце строки.

Якорение и слова

Мы рассмотрели якоря начала (^) и конца строки ($) выше. Однако вы можете использовать другие якоря для работы с границами слов.

В этом контексте слово — это последовательность символов, ограниченная пробелом (начало или конец строки). Таким образом, «psy66oh» будет считаться словом, хотя вы не найдете его в словаре.

Начало привязки к слову (\<); обратите внимание, что он указывает влево, к началу слова. Допустим, имя было ошибочно набрано строчными буквами. Мы можем использовать параметр grep -i, чтобы выполнить поиск без учета регистра и найти имена, начинающиеся с «h».

Набираем следующее:

grep -E -i 'h' geeks.txt

Это находит все вхождения «h», а не только в начале слов.

grep -E -i '\<h' geeks.txt

Это находит только те, которые находятся в начале слов.

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

grep -E 'y' geeks.txt

Это находит все вхождения «y», где бы оно ни появлялось в словах.

Теперь мы вводим следующее, используя якорь конца слова (/>) (который указывает вправо или конец слова):

grep -E 'y\>' geeks.txt

Вторая команда дает желаемый результат.

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

grep -E '\bGlenn\b' geeks.txt
grep -E '\Bway\B' geeks.txt

Дополнительные классы персонажей

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

Вы можете использовать все следующее:

  • A–Z: все прописные буквы от A до Z.
  • a-z: все строчные буквы от a до z.
  • 0–9: все цифры от нуля до девяти.
  • d-p: все строчные буквы от d до p. Эти стили в свободном формате позволяют определить собственный диапазон.
  • 2–7: все числа от двух до семи.

Вы также можете использовать любое количество классов символов в шаблоне поиска. Следующий шаблон поиска соответствует последовательностям, которые начинаются с буквы J, за которой следует буква o или s, а затем либо e, h, l или s:

grep -E 'J[os][ehls]' geeks.txt

В нашей следующей команде мы будем использовать спецификатор диапазона a-z.

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

  • H: последовательность должна начинаться с буквы H.
  • [a-z]: следующим символом может быть любая строчная буква из этого диапазона.
  • *: здесь звездочка обозначает любое количество строчных букв.
  • мужчина. Последовательность должна заканчиваться на мужчина.

Объединяем все это в следующую команду:

grep -E 'H[a-z]*man' geeks.txt

Нет ничего непроницаемого

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

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

Например, посмотрите на эту команду:

grep -E '^([0-9]{4}[- ]){3}[0-9]{4}|[0-9]{16}' geeks.txt

С чего бы вы начали распутывать это? Мы начнем с самого начала и рассмотрим по одному фрагменту за раз:

  • ^: начало якоря строки. Итак, наша последовательность должна быть первой в строке.
  • ([0-9]{4}[- ]): скобки объединяют элементы шаблона поиска в группу. Другие операции могут быть применены к этой группе в целом (подробнее об этом позже). Первый элемент — это класс символов, содержащий диапазон цифр от нуля до девяти [0-9]. Таким образом, наш первый символ — это цифра от нуля до девяти. Далее у нас есть интервальное выражение, содержащее число четыре {4}. Это относится к нашему первому символу, который, как мы знаем, будет цифрой. Таким образом, первая часть шаблона поиска теперь состоит из четырех цифр. За ним может следовать пробел или дефис ([- ]) из другого класса символов.
  • {3}: описатель интервала, содержащий число три, следует сразу за группой. Он применяется ко всей группе, поэтому наш шаблон поиска теперь состоит из четырех цифр, за которыми следует пробел или дефис, который повторяется три раза.
  • [0-9]: Далее у нас есть еще один класс символов, который содержит диапазон цифр от нуля до девяти [0-9]. Это добавит еще один символ в шаблон поиска, и это может быть любая цифра от нуля до девяти.
  • {4}: другое выражение интервала, содержащее число четыре, применяется к предыдущему символу. Это означает, что символ становится четырьмя символами, каждый из которых может быть любой цифрой от нуля до девяти.
  • |: Оператор чередования говорит нам, что все, что находится слева от него, является полным шаблоном поиска, а все, что находится справа, — новым шаблоном поиска. Итак, эта команда на самом деле ищет любой из двух шаблонов поиска. Первый состоит из трех групп из четырех цифр, за которыми следует пробел или дефис, а затем добавляются еще четыре цифры.
  • [0–9]: второй шаблон поиска начинается с любой цифры от нуля до девяти.
  • {16}: оператор интервала применяется к первому символу и преобразует его в 16 символов, все из которых являются цифрами.

Итак, наш шаблон поиска будет искать одно из следующего:

  • Четыре группы по четыре цифры, каждая из которых разделена пробелом или дефисом (-).
  • Одна группа из шестнадцати цифр.

Результаты показаны ниже.

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

Помедленней

Сложность, как правило, состоит из простоты, скрепленной вместе. Как только вы поймете основные строительные блоки, вы сможете создавать эффективные, мощные утилиты и развивать новые ценные навыки.