Использование регулярных выражений (regex) в Linux

12 ноября, 2010 Рубрики: bash, Linux, основы Linux

использование регулярных выражений в LinuxДоброго времени, гости!

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

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

1. Традиционные регулярные выражения (они же основные, базовые и basic regular expressions (BRE))

  • синтаксис данных выражений определен, как устаревший, но тем не менее до сих пор широко распространен и  используется многими утилитами UNIX
  • Основные регулярные выражения включают в себя следующие метасимволы (об их значениях ниже):
    • .
    • [ ]
    • [^ ]
    • ^
    • $
    • *
    • \{ \} — первоначальный вариант для { } (в расширенных)
    • \( \) — первоначальный вариант для ( )(в расширенных)
    • \n, где n — номер от 1 до 9
  • Особенности использования данных метасимволов:
    • Звёздочка должна следовать после выражения, соответствующего единичному символу. Пример: [xyz]*.
    • Выражение \(блок\)* следует считать неправильным. В некоторых случаях оно соответствует нулю или более повторений строки блок. В других оно соответствует строке блок*.
    • Внутри символьного класса специальные значения символов, в основном, игнорируются. Особые случаи:
    • Чтобы добавить символ ^ в набор, его следует поместить туда не первым.
    • Чтобы добавить символ -в набор, его следует поместить туда первым или последним. Например:
      • шаблон DNS-имени, куда могут входить буквы, цифры, минус и точка-разделитель: [-0-9a-zA-Z.];
      • любой символ, кроме минуса и цифры: [^-0-9].
    • Чтобы добавить символ [ или ]в набор, его следует поместить туда первым. Например:
      • [][ab] соответствует ], [, a или b.

2. Расширенные регулярные выражения (они же extended regular expressions (ERE))

  • Синтаксис данных выражений аналогичен синтаксису основных выражений, за исключением:
    • Отменено использование обратной косой черты для метасимволов { } и ( ).
    • Обратная косая черта перед метасимволом отменяет его специальное значение.
    • Отвергнута теоретически нерегулярная конструкция \n.
    • Добавлены метасимволы +, ?, |.

3. Регулярные выражения, совместимые с Perl (они же Perl-compatible regular expressions (PCRE))

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

Далее немного поговорим о синтаксисе регулярных выражений.

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

Правила поиска определяются следующими операциями:

Перечисление |

Вертикальная черта (|) разделяет допустимые варианты, можно сказать – логическое ИЛИ. Например, «gray|grey» соответствует gray или grey.

Группировка или объединение ( )

Круглые скобки используются для определения области действия и приоритета операторов. Например, «gray|grey» и «gr(a|e)y» являются разными образцами, но они оба описывают множество, содержащее gray и grey.

Квантификация {} ? * +

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

{m,n}

общее выражение, повторений может быть от m до n включительно.

{m,}

общее выражение, m и более повторений.

{,n}

общее выражение, не более n повторений.

{n}

ровно n повторений.

?

Знак вопроса означает 0 или 1 раз, то же самое, что и {0,1}. Например, «colou?r» соответствует и color, и colour.

*

Звёздочка означает 0, 1 или любое число раз ({0,}). Например, «go*gle» соответствует ggle, gogle, google и др.

+

Плюс означает хотя бы 1 раз ({1,}). Например, «go+gle» соответствует gogle, google и т. д. (но не ggle).

Конкретный синтаксис данных регулярных выражений зависит от реализации. (то есть в базовых регулярных выражениях символы { и } – экранируются обратным слешем)

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

. соответствует одному любому символу
[что-то] Соответствует любому единичномусимволу из числа заключённых в скобки. При этом:Символ «-» интерпретируется буквально только в том случае, если он расположен непосредственно после открывающей или перед закрывающей скобкой: [abc-] или [-abc]. В противном случае, он обозначает интервал символов.Например, [abc] соответствует «a», «b» или «c». [a-z] соответствует буквам нижнего регистра латинского алфавита. Эти обозначения могут и сочетаться: [abcq-z] соответствует a, b, c, q, r, s, t, u, v, w, x, y, z.Чтобы установить соответствие символам «[» или «]», достаточно, чтобы закрывающая скобка была первым символом после открывающей: [][ab] соответствует «]», «[», «a» или «b».Если значение в квадратных скобах предварено символом ^, то значение выражения соответствует единичному символу из числа тех, которых нет в скобках. Например, [^abc] соответствует любому символу, кроме «a», «b» или «c». [^a-z] соответствует любому символу, кроме символов нижнего регистра в латинском алфавите.
^ Соответствует началу текста (или началу любой строки, если режим построчный).
$ Соответствует концу текста (или концу любой строки, если режим построчный).
\(\) или ( ) Объявляет «отмеченное подвыражение» (сгруппированное выражение), которое может быть использовано позже (см. следующий элемент: \n). «Отмеченное подвыражение» также является «блоком». В отличие от других операторов, этот (в традиционном синтаксисе) требует бэкслеша, в расширенном и Perl символ \ – не нужен.
\n Где n — это цифра от 1 до 9; соответствует n-му отмеченному подвыражению (например (abcd)\0, то есть символы abcd отмечены нулем). Эта конструкция теоретически нерегулярна, она не была принята в расширенном синтаксисе регулярных выражений.
*
  • Звёздочка после выражения, соответствующего единичному символу, соответствует нулю или более копий этого (предшествующего) выражения. Например, «[xyz]*» соответствует пустой строке, «x», «y», «zx», «zyx», и т. д.
  • \n*, где n — это цифра от 1 до 9, соответствует нулю или более вхождений для соответствия n-го отмеченного подвыражения. Например, «\(a.\)c\1*» соответствует «abcab» и «abcaba», но не «abcac».

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

\{x,y\} Соответствует последнему (предстоящему) блоку, встречающемуся не менее x и не более y раз. Например, «a\{3,5\}» соответствует «aaa», «aaaa» или «aaaaa». В отличие от других операторов, этот (в традиционном синтаксисе) требует бэкслеша.
.* Обозначение любого количества любых символов между двумя частями регулярного выражения.

Метасимволы нам помогают использовать различные соответствия. Но как же представить метасимвол обычным символом, то есть символ [ (квадратная скобка) значением квадратной скобки? Просто:

  • необходимо предварить (экранировать) метасимвол (. * + \ ? [ ] { } ) обратным слешем. Например \. или \[

Для упрощения задания некоторых наборов символов, их объединили в т.н.классы и категории символов. POSIX стандартизовал объявление некоторых классов и категорий символов, как показано в следующей таблице:

POSIX класс аналогично обозначение
[:upper:] [A-Z] символы верхнего регистра
[:lower:] [a-z] символы нижнего регистра
[:alpha:] [A-Za-z] символы верхнего и нижнего регистра
[:alnum:] [A-Za-z0-9] цифры, символы верхнего и нижнего регистра
[:digit:] [0-9] цифры
[:xdigit:] [0-9A-Fa-f] шестнадцатеричные цифры
[:punct:] [.,!?:…] знаки пунктуации
[:blank:] [ \t] пробел и TAB
[:space:] [ \t\n\r\f\v] символы пропуска
[:cntrl:] символы управления
[:graph:] [^ \t\n\r\f\v] символы печати
[:print:] [^\t\n\r\f\v] символы печати и символы пропуска

В regex есть такое понятие как:

Жадность regex

Постараюсь описать как можно понятней. Допустим, мы хотим найти все HTML теги в каком-то тексте. Локализовав задачу, мы хотим найти значения заключенные между < и >, вместе с этими самыми скобками. Но мы знаем, что теги имеют разную длину и самих тегов, как минимум штук 50. Перечислять их все , заключив в метасимволы [] – задача слишком трудоемкая. Но мы знаем, что у нас есть выражение .* (точка звездочка), характеризующее любое число любых символов в строке. С помощью данного выражения мы попытаемся найти в тексте (<p>Итак, <strong>Как создать RAID уровня 10/50 на контроллере LSI MegaRAID (актуально и для: Intel SRCU42x, Intel SRCS16):</strong><span id=”more-2058″></span></p>) все значения между < и >. В результате, этому выражению будет соответствовать ВСЯ строка. почему, потому что регекс – ЖАДЕН и старается захватить ЛЮБОЕ ВСЕ количество символов между < и >, соответственно вся строка, начиная <p>Итак,… и заканчивая …</span></p> будет принадлежать данному правилу!

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

  • учесть символы, не соответствующие желаемому образцу (например: <[^>]*> для вышеописанного случая)
  • избавить от жадности, добавив определении квантификатора, как нежадного:
    • *? – «не жадный» («ленивый») эквивалент *
    • +? – «не жадный» («ленивый») эквивалент +
    • {n,}? – «не жадный» («ленивый») эквивалент {n,}
    • .*? – «не жадный» («ленивый») эквивалент .*

Все вышенаписанное хочу дополнить синтаксисом расширенных регулярных выражений:

Регулярные выражения в POSIX аналогичны традиционному Unix-синтаксису, но с добавлением некоторых метасимволов:

+

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

?

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

|

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

Также было отменено использование обратной косой черты: \{…\} становится {…} и \(…\) становится (…).

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

[regex@regexp k-max.name]$ cat text1
1 apple
2 pear
3 banana
[regex@regexp k-max.name]$ grep p text1
1 apple
2 pear
[regex@regexp k-max.name]$ grep pea text1
2 pear
[regex@regexp k-max.name]$ grep "p*" text1
1 apple
2 pear
3 banana
[regex@regexp k-max.name]$ grep "pp*" text1
1 apple
2 pear
[regex@regexp k-max.name]$ grep "x" text1
[regex@regexp k-max.name]$ grep "x*" text1
1 apple
2 pear
3 banana
[regex@regexp k-max.name]$ cat text1 | grep "l\|n"
1 apple
3 banana
[regex@regexp k-max.name]$ echo -e "find an\n* here" | grep "\*"
* here
[regex@regexp k-max.name]$ grep "pp\+" text1 # строки, с содержанием одной р и 1 и более р
1 apple
[regex@regexp k-max.name]$ grep "pl\?e" text1
1 apple
2 pear
[regex@regexp k-max.name]$ grep "pl\?e" text1 # pe с возможным символом l
1 apple
2 pear
[regex@regexp k-max.name]$ grep "p.*r" text1 # p, в строках где есть r
2 pear
[regex@regexp k-max.name]$ grep "a.." text1 # строки с a, за которой следует как минимум 2 символа
1 apple
3 banana
[regex@regexp k-max.name]$ grep "\(an\)\+" text1 # Поиск более повторения an
3 banana
[regex@regexp k-max.name]$ grep "an\(an\)\+" text1 # поиск 2х повторений an
3 banana
[regex@regexp k-max.name]$ grep "[3p]" text1 # поиск строк, где есть 3 или p
1 apple
2 pear
3 banana
[regex@regexp k-max.name]$ echo -e "find an\n* here\nsomewhere." | grep "[.*]"
* here
somewhere.
[regex@regexp k-max.name]$ # Ищет символы от 3 до 7
[regex@regexp k-max.name]$ echo -e "123\n456\n789\n0" | grep "[3-7]"
123
456
789
[regex@regexp k-max.name]$ # Ищем цифру, за которой до конца строки нет букв n и r
[regex@regexp k-max.name]$ grep "[[:digit:]][^nr]*$" text1
1 apple
[regex@regexp k-max.name]$ sed -e '/\(a.*a\)\|\(p.*p\)/s/a/A/g' text1 # замена а на А во всех строках, где после а идет а или после р идет р
1 Apple
2 pear
3 bAnAnA
[regex@regexp k-max.name]$ sed -e '/^[^lmnXYZ]*$/s/ear/each/g' text1 # замена ear на each в строках не начинающихся на lmnXYZ
1 apple
2 peach
3 banana
[regex@regexp k-max.name]$ echo "First. A phrase. This is a sentence." |\ # замена последнего слова в предложении на LAST WORLD.
&amp;amp;gt; sed -e 's/ [^ ]*\./ LAST WORD./g'
First. A LAST WORD. This is a LAST WORD.

С Уважением, Mc.Sim!




Теги: , , , , , ,

13 комментариев к “Использование регулярных выражений (regex) в Linux”

  1. артем
    8 августа, 2011 at 19:16
    1

    Например, «go*gle» соответствует ggle, gogle, google и др.

    ggle не может соответствовать.
    gogle
    go[any char]gle

    • 9 августа, 2011 at 09:59
      2

      Согласен. Ошибочка. Спасибо – исправлено :)

      • Олег
        30 июля, 2013 at 14:46
        3

        2артем и 2 Mc.Sim
        * это квантификатор, а не любой символ. Любой символ это ‘.’
        go*gle означает g(o или ПУСТО или “любое количество символов ‘o'” )gle
        согласно этому ggle — тоже будет найден

        • 16 августа, 2013 at 14:23
          4

          думаю, что Ваш пример не совсем верен, я бы ваш фразы перефразировал из:

          Любой символ это ‘.’

          в

          Любой один (не ноль) символ это ‘.’

          а так же:

          go*gle означает g(o или ПУСТО или «любое количество символов ‘o’» )gle

          в

          go*gle означает gо( ПУСТО или «любое количество символов ‘o’ или любого другого символа» )gle

          • Олег
            29 августа, 2013 at 15:26
            5

            мне жаль, но вы ошибаетесь. Квантификатор относится к последнему символу и 0 означает, что его нет
            Т.е. ggle будет найден по строке запроса go*gle как и по go{0,}gle , которые идентичны

  2. Жека
    10 февраля, 2013 at 23:24
    6

    Я новичок, начал читать ваши статьи с самого начала, все было крайне понятно (за что и крайне благодарен =) ), но вот дошел до этой статьи, и совсем не понял: Что это? зачем? Где используется как? Почему? Откуда? – Извините за такое количество, возможно, даже немного однотипных, вопросов, просто хотел показать свою озадаченность увиденным =-O :-D
    Если эту статью рассматривать как шпаргалку для юзверей с опытом, то скорей всего информация подана очень сжато и правильно, но если рассматривать с точки зрения новичка, то совсем не понятно – ничего.
    Все-таки для новичков, лучше начать с применения: “где используется, почему, где не используется”? Сразу с начала статьи выдается крайне огромное количество непонятных терминов, и дальше их количество неуклонно растет. Может лучше “не типизировать” Регулярные выражения, если они все используются, а просто списком представить. И в конце статьи когда у нас вылезли глаза от “экранирования, жадности, ленивости, POSIX” и прочего, то мы видим длиный монолитный список примеров. Поэтому мне кажется, более уместным, использование стандартного подхода, “кусок информации – пример”. Так и не понял, для поиска они используются , или для шел скриптов, или как… *UNKNOWN* Откуда взялся там Perl, почему не Pyton? :-D
    Если немного переработаете статью в данном русле, то думаю это очень многим поможет.
    Спасибо за столь полезный блог *BRAVO* я тут надолго засел :)

    • 16 марта, 2013 at 16:26
      7

      Вы верно поняли, что статья рассчитана на пользователей с опытом.
      Если в двух словах, то можно описать так:
      Данная технология используется при работе с текстом (точнее со строками в каком-то тексте).
      При этом, необходимо понимать, что вывод любой консольной команды в linux – это текст.
      В linux практически все команды, работающие с текстом имеют формат:
      $ команда параметр1 параметр2 ...
      Например, команда grep имеет формат:
      grep [ПАРАМЕТР]... ШАБЛОН [ФАЙЛ]
      собственно, в поле ШАБЛОН и используются регулярные выражения.
      Используя шаблон мы тем самым осуществляем поиск ШАБЛОНА в ФАЙЛЕ. Соответственно, если найдена строка, соответствующая регулярному выражению, то она выводится на экран. Но это очень топорное объяснение.
      В примерах использования команды как раз приведено действие регулярных выражений.

      В языках программирования Perl данная технология применяется в полной мере, собственно, как и в указанном Вами Pyton.
      По регулярным выражениям пишут книги по 600 страниц ) Примите это просто как шпаргалку. Кроме того в интернете много можно почитать об этом. Но в целом, вполне возможно в перспективе руки когда-нибудь дойдут до статьи…

  3. Andrew
    7 мая, 2014 at 23:51
    8

    Звёздочка означает 0, 1 или любое число раз ({0,}). Например, «g*gle» соответствует ggle, gogle, google и др.

    Видимо Вы имели ввиду «go*gle» (o – пропустили)

  4. 24 июня, 2014 at 20:24
    9

    спасибо всем, google поправлен ))

  5. Игнат
    21 декабря, 2014 at 18:30
    10

    Как найти трехзначное шестнадцатиричное число с помощью grep?? Помогите очень надо

    • 8 апреля, 2015 at 20:19
      11

      Думаю, что тут нужно более предметное понимание.
      Пример текста, например.

  6. KillSer2014
    3 апреля, 2015 at 10:51
    12

    Mc.Sim, во-первых спасибо за столь обстоятельно объяснение regex
    такой вопрос, я пытаюсь найти все файлы, имена которых содержат “кракозябры”, например:

    # ls -1
    ®¨®§¶
    ®¨®§¶.jpg
    test.txt
    рус.txt

    для поиска использую find -regex. Сразу скажу, convmv использовать не получается,так как требуется двойное преобразование
    как расшифровать “кракозябры” я знаю, а вот написать правильный regex не получается.
    пытаюсь сделать так:
    find ./ -maxdepth 1 -regextype posix-extended -not -regex "\.\/[a-zA-Zа-яА-Я\d]"
    регуляркой хочу найти всё что имеет первые 2 два символа “./”, затем символ анг/рус алфавита либо цифра. Всё что идёт после должно игнорироваться, потому что как правило “испоганено” имя файла, но не окончание.

    Подскажите, где ошибка в регулярном выражении

    pastebin

    • 7 июня, 2015 at 22:04
      13

      Актуален ли еще вопрос?

Написать комментарий