Мне достаточно часто задают вопросы связанные не столько с самим 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 -
Продолжение: Отрицательные группы и якоря.
21.11.2009 в 6:23
Вася! давай исчё!
21.11.2009 в 10:32
Обязательно 🙂 Штук 5 еще наготове, только подправить перед постингом.
21.11.2009 в 23:50
Как раз учу PowerShell, и будет полезно узнать базовые понятия про регэкспы. Спасибо за статью.
21.11.2009 в 23:54
You are welcome, это только начало 🙂
23.11.2009 в 11:20
круто. интересно будет попробовать в новом ForeFront for Exchange 2010 регулярные выражения для фильтрации HELO-заголовков. а то сейчас у меня фильтруется на базе регулярных выраженияй перла =)
23.11.2009 в 11:22
>на базе регулярных выраженияй перла
Хм. Обычно в таких продуктах используется опитимизированная и упрощенная реализация регулярных выражений. А на базовом уровне разницы между perl и .NET большой не будет. Или речь не об этом?
23.11.2009 в 16:44
речь о следующем — я сейчас использую ORF для борьбы со спамом. в нём есть отличный фильтр, который может фильтровать входящие smtp-соединения по HELO-заголовку. ну чтобы отсекать всякие сервера, имена которых похожи на ip-адреса или на имена которые выдаются провайдерами частным клиентам (adsl, vpn, pppoe итд). в FF for Exchange 2007 ничего аналогичного этому фильтру не было. возможно в 2010 появилось (ещё не смотрел) и можно будеть попробовать пофильтровать входящий smtp-трафик через PoSh. если получится — то, считай, можно будет отказаться от ORF и оставить только FF for Exchange.
23.11.2009 в 16:50
Ммм.. То есть ты думаешь что в FF появится возможность фильтровать с помощью скриптов PS? Или что там будут просто правила с регекспами?
23.11.2009 в 18:09
не знаю. учитывая, что туда прикрутили какой-то антиспамовский движок, то можно предположить что может быть и фильтровать можно через регулярные выражения либо в самом движке, либо через скрипты PoSh.
24.11.2009 в 10:18
отличная статья.
Хочется ещё больше!
26.11.2009 в 11:42
Разжеванный about_ это хорошо.
Кстати, почему работает
Get-ChildItem -Recurse | where {$_.Name -notmatch «[d]»}
И не работает
Get-ChildItem -Recurse | where {$_.Name -match «[^d]»}
?
26.11.2009 в 12:02
Ну это только начало, дальше будет больше и интереснее чем в about 🙂
Get-ChildItem -Recurse | where {$_.Name -notmatch «[d]«} #Ищет все где не встречается буква d (кстати можно без [] обойтись).
Get-ChildItem -Recurse | where {$_.Name -match «[^d]«} #Ищет всё где _хотябы_одна_ буква не является буквой d.
«при сравнении с регулярным выражением ищется лишь вхождение строки, полное совпадение текста необязательно» То есть если выражение совпадает хотя бы с частью строки, с любой — совпадение есть. Как это обходить — расскажу дальше.
26.11.2009 в 12:08
Кстати, а можно ли заставить упомянутый командлет принимать регулярные выражения в -Filter ?
26.11.2009 в 17:18
Простой ответ: Нельзя, сделано было из за того что регулярные выражения гораздо менее производительные чем более простые файловые маски.
Сложный ответ: Можно, например написав proxy-командлет, в котором переписать -filter (или добавить -regexfilter например) для поддержки регулярных выражений. Как это сделать — искать либо в зарубежных блогах, либо ждать пока я напишу об этом по русски (благо хороший пример) 🙂 Записал себе в todo 🙂
26.11.2009 в 12:06
Разобрался, очевидно Get-ChildItem -Recurse | where {$_.Name -match «[^d]«}
будет работать лишь для файла, имя которого состоит из единственной литеры d (или D), а при вхождении какого-либо другого символа в имя файла проверка на [^d] считается успешно пройденной.
26.11.2009 в 12:07
Именно.
26.11.2009 в 12:07
Оу, чуть чуть опоздал, спасибо.)
26.11.2009 в 16:11
[…] @ 16:11 Продолжаем разговор о регулярных выражениях. В предыдущем посте я рассказал об основах, а в этом рассмотрим некоторые […]
4.12.2009 в 11:26
[…] Введение […]
11.12.2009 в 18:23
[…] Введение […]
18.12.2009 в 12:51
[…] Введение […]
24.12.2009 в 12:04
[…] Регулярные выражения – Жадность 24.12.2009 — Xaegr Просто отличное название для очередной статьи о регулярных выражениях в блоге посвященном PowerShell Но оно действительно подходит лучше всего. Сегодня мы поговорим об одной важной концепции регулярных выражений. От чего зависит сколько символов будет захвачено количественным модификатором с варьирующейся длинной? Именно от жадности Если вы наткнулись на пост случайно, то сначала лучше ознакомьтесь с предыдущими постами серии – 1,2,3,4,5. […]
28.12.2009 в 12:09
[…] выражений разумеется Мы успели познакомится с основами, отрицательными группами и якорями, квантификаторами, […]
24.2.2010 в 11:00
[…] на посты которые рекомендуется прочитать сначала: 1, 2, 3, 4, 5, 6, […]