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

regexp-0 Мне достаточно часто задают вопросы связанные не столько с самим PowerShell, сколько с применением в нем регулярных выражений. Это и понятно — регулярные выражения (или если сокращенно “регэкспы” (regexp, regular expressions)) обладают огромной мощью, и способны сильно упростить жизнь системного администратора или программиста. Однако в мире системного администрирования Windows они мало известны и непопулярны — в cmd.exe практически единственная возможность их применения это утилита findstr.exe, которая обладает очень маленьким функционалом и использует жутко урезанный диалект регулярных выражений. В VBScript функционал регулярных выражений тоже хорошо запрятан, и практически не используется. А вот в PowerShell, авторы языка позаботились о том чтобы регулярные выражения были легко доступны, удобны в использовании и максимально функциональны. Тем более что с последним пунктом всё оказалось достаточно просто — PowerShell использует реализацию регулярных выражений .NET, а она является одной из самых функциональных и производительных, и даже способна потягаться даже с признанным лидером в этой области — perl’ом🙂

Итак, перейдем к делу. Что же такое регулярные выражения? Я не помню правильных и сухих определений из умных книжек, да и незачем, кому интересно — прочитает их сам🙂 Регулярные выражения это специальный мини-язык служащий для разбора (parsing) текстовых данных. С его помощью можно разделять строки на компоненты, выбирать нужные части строк для дальнейшей обработки, производить замены, и всё это с огромной гибкостью и точностью.

Впрочем… знакомство с регулярными выражениями лучше начать не с них, а с более простой технологии служащей подобным целям, с которой знакомы все Windows администраторы – с подстановочных символов. Наверняка вы не раз выполняли команду dir, и указывали ей в качестве аргумента маску файла, например *.exe. В данном случае звёздочка означает “любое количество любых символов”. Аналогично можно использовать и знак вопроса, он будет означать “один любой символ”, то есть dir ??.exe выведет все файлы с расширением .exe и именем из двух символов. В PowerShell’овской реализации подстановочных символов можно применять и еще одну конструкцию — группы символов. Так например [a-f] будет означать “один любой символ от a до f (a,b,c,d,e,f)”, а [smw] любую из трех букв (s, m или w). Таким образом команда get-childitem [smw]??.exe выведет файлы с расширением .exe, у которых имя состоит из трех букв, и первая буква либо s, либо m, либо w. Неплохо, неправда ли? Так вот, по сравнению с возможностями регулярных выражения — это детский лепет🙂 Но начнём с малого🙂

Для начала изучения мы будем использовать оператор PowerShell -match, который позволяет сравнивать текст слева от него, с регулярным выражением справа. В случае если текст подпадает под регулярное выражение, оператор выдаёт True, иначе — False.

PS C:\> "PowerShell" -match "Power"
True

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

PS C:\> "Shell" -match "Power"
False
PS C:\> "PowerShell" -match "rsh"
True

Еще одна тонкость: оператор -match по умолчанию не чувствителен к регистру символов (как и другие текстовые операторы в PowerShell), если же вам нужна чувствительность к регистру, используйте -cmatch:

PS C:\> "PowerShell" -cmatch "rsh"
False

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

PS C:\> Get-Process | where {$_.name -match "sy[ns]"}

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    165      11     2524       8140    79     0,30   5228 mobsync
    114      10     3436       3028    83    50,14   3404 SynTPEnh
    149      11     2356        492    93     0,06   1592 SynTPStart
    810       0      116        380     6               4 System

И диапазоны в этих группах:

PS C:\> "яблоко","апельсин","груша","абрикос" -match "а[а-п]"
апельсин
абрикос

Кстати тут я в левой части оператора -match поместил массив строк, и он соответственно вывел лишь те строки, которые подошли под регулярное выражение.

Разумеется перечисления символов можно комбинировать, например группа [агдэ-я] будет означать “А или Г или Д или любой символ от Э до Я включительно”. Но гораздо интереснее использовать диапазоны для определения целых классов символов. Например [а-я] будет означать любую букву русского алфавита, а [a-z] английского. Аналогично можно поступать с цифрами — следующая команда выведет все процессы в именах которых встречаются цифры:

PS C:\> Get-Process | where {$_.name -match "[0-9]"}

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     93      10     1788       2336    70     1,25    548 FlashUtil10c
    158      12     6500       1024    96     0,14   3336 smax4pnp
     30       6      764        160    41     0,02   3920 TabTip32

Так как эта группа используется достаточно часто, для неё была выделена специальная последовательность — \d (от слова digit). По смыслу она полностью идентична [0-9], но гораздо короче🙂

PS C:\> Get-Process | where {$_.name -match "\d"}

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     93      10     1788       2336    70     1,25    548 FlashUtil10c
    158      12     6500       1024    96     0,14   3336 smax4pnp
     30       6      764        160    41     0,02   3920 TabTip32

Так же последовательность была выделена для группы “любые буквы любого алфавита, любые цифры, или символ подчеркивания” эта группа обозначается как \w (от word) она примерно эквивалентна конструкции [a-zа-я_0-9]\w еще входят символы других алфавитов которые используются для написания слов).

Еще вам наверняка встретится другая популярная группа: \s — “пробел, или другой пробельный символ” (например символ табуляции). Сокращение от слова space. В большинстве случаев вы можете обозначать пробел просто как пробел🙂 но эта конструкция добавляет читабельности регулярному выражению.

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

Все вышеперечисленные конструкции можно использовать как отдельно, так и в составе групп, например [\s\d] будет соответствовать любой цифре или пробелу. Если вы хотите указать внутри группы символ - (тире/минус) то надо либо экранировать его символом \ (обратный слеш), либо поставить его в начале группы, чтобы он не был случайно истолкован как диапазон:

PS C:\> "?????","Word","123","-" -match "[-\d]"
123
-

Продолжение: Отрицательные группы и якоря.

Опубликовано в Regular Expressions. Метки: . 24 комментария »

комментария 24 to “Регулярные выражения — Введение”

  1. abigor Says:

    Вася! давай исчё!

    • Xaegr Says:

      Обязательно🙂 Штук 5 еще наготове, только подправить перед постингом.

  2. x1shn1k Says:

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

  3. Stanislav Buldakov Says:

    круто. интересно будет попробовать в новом ForeFront for Exchange 2010 регулярные выражения для фильтрации HELO-заголовков. а то сейчас у меня фильтруется на базе регулярных выраженияй перла =)

  4. Xaegr Says:

    >на базе регулярных выраженияй перла
    Хм. Обычно в таких продуктах используется опитимизированная и упрощенная реализация регулярных выражений. А на базовом уровне разницы между perl и .NET большой не будет. Или речь не об этом?

  5. Stanislav Buldakov Says:

    речь о следующем — я сейчас использую ORF для борьбы со спамом. в нём есть отличный фильтр, который может фильтровать входящие smtp-соединения по HELO-заголовку. ну чтобы отсекать всякие сервера, имена которых похожи на ip-адреса или на имена которые выдаются провайдерами частным клиентам (adsl, vpn, pppoe итд). в FF for Exchange 2007 ничего аналогичного этому фильтру не было. возможно в 2010 появилось (ещё не смотрел) и можно будеть попробовать пофильтровать входящий smtp-трафик через PoSh. если получится — то, считай, можно будет отказаться от ORF и оставить только FF for Exchange.

  6. Xaegr Says:

    Ммм.. То есть ты думаешь что в FF появится возможность фильтровать с помощью скриптов PS? Или что там будут просто правила с регекспами?

    • Stanislav Buldakov Says:

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

  7. Vasiliy Says:

    отличная статья.
    Хочется ещё больше!

  8. Юрий Says:

    Разжеванный about_ это хорошо.
    Кстати, почему работает
    Get-ChildItem -Recurse | where {$_.Name -notmatch «[d]»}
    И не работает
    Get-ChildItem -Recurse | where {$_.Name -match «[^d]»}
    ?

    • Xaegr Says:

      Ну это только начало, дальше будет больше и интереснее чем в about🙂

      Get-ChildItem -Recurse | where {$_.Name -notmatch «[d]«} #Ищет все где не встречается буква d (кстати можно без [] обойтись).

      Get-ChildItem -Recurse | where {$_.Name -match «[^d]«} #Ищет всё где _хотябы_одна_ буква не является буквой d.

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

      • Юрий Says:

        Кстати, а можно ли заставить упомянутый командлет принимать регулярные выражения в -Filter ?

        • Xaegr Says:

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

          Сложный ответ: Можно, например написав proxy-командлет, в котором переписать -filter (или добавить -regexfilter например) для поддержки регулярных выражений. Как это сделать — искать либо в зарубежных блогах, либо ждать пока я напишу об этом по русски (благо хороший пример)🙂 Записал себе в todo🙂

    • Юрий Says:

      Разобрался, очевидно Get-ChildItem -Recurse | where {$_.Name -match «[^d]«}
      будет работать лишь для файла, имя которого состоит из единственной литеры d (или D), а при вхождении какого-либо другого символа в имя файла проверка на [^d] считается успешно пройденной.

    • Юрий Says:

      Оу, чуть чуть опоздал, спасибо.)

  9. Регулярные выражения – Отрицательные группы и якоря « PowerShell и другие скрипты Says:

    […] @ 16:11 Продолжаем разговор о регулярных выражениях. В предыдущем посте я рассказал об основах, а в этом рассмотрим некоторые […]

  10. Регулярные выражения – Жадность « PowerShell и другие скрипты Says:

    […] Регулярные выражения – Жадность 24.12.2009 — Xaegr Просто отличное название для очередной статьи о регулярных выражениях в блоге посвященном PowerShell Но оно действительно подходит лучше всего. Сегодня мы поговорим об одной важной концепции регулярных выражений. От чего зависит сколько символов будет захвачено количественным модификатором с варьирующейся длинной? Именно от жадности Если вы наткнулись на пост случайно, то сначала лучше ознакомьтесь с предыдущими постами серии – 1,2,3,4,5. […]

  11. Регулярные выражения – Select-String « PowerShell и другие скрипты Says:

    […] выражений разумеется Мы успели познакомится с основами, отрицательными группами и якорями, квантификаторами, […]

  12. Регулярные выражения – Switch « PowerShell и другие скрипты Says:

    […] на посты которые рекомендуется прочитать сначала: 1, 2, 3, 4, 5, 6, […]


Обсуждение закрыто.

%d такие блоггеры, как: