Windows PowerShellНаписание регулярных выражений

Дон Джонс (Don Jones)

192.168.4.5. \\Server57\Share. johnd@contoso.com. Безусловно, вы узнали три этих элемента: это IP-адрес, путь универсального имени (UNC) и адрес электронной почты. Распознать их форматы для вас не представляет труда. Сочетание цифр, обратной косой черты, символа @ и других условных знаков определяет тип данных, представленных

символами строки. Немного подумав, вы сможете быстро понять, что 192.168 представляет собой допустимый IP-адрес, что 7\\Server2\\Share — это допустимый UNC-путь, а joe@contoso — правильный адрес электронной почты.

Компьютерам, к сожалению, приходится «думать» намного больше, чтобы «понять» сложные форматы, подобные этим. Именно в этом могут помочь регулярные выражения. Регулярное выражение — это строка кода, написанного на особом языке регулярных выражений, который помогает компьютеру идентифицировать строки определенного формата, такие как строки IP-адреса, UNC или адреса электронной почты. Хорошо написанное регулярное выражение способно обеспечить использование сценария Windows PowerShellTM для принятия допустимых или отклонения недопустимых данных, формат которых не отвечает заданному.

Простое совпадение

Оператор –match в Windows PowerShell сравнивает сроку с регулярным выражением (regex) и затем возвращает значение «Истина» или «Ложь» в зависимости от совпадения строки с выражением. Самое простое регулярное выражение даже может не иметь какого-то специального синтаксиса — буквенных символов будет достаточно. Пример:

"Microsoft" –match "soft"
"Software" –match "soft"
"Computers" –match "soft"

При выполнении в Windows PowerShell, первые два выражения возвращают «Истина», а третье — «Ложь». В каждом случае после строки указывается оператор –match, за которым следует регулярное выражение. По умолчанию, регулярное выражение перемещается по строке для получения совпадения. Слово «soft» является частью слов «Software» и «Microsoft», но находится в разных позициях. Также обратите внимание, что по умолчанию регулярное выражение не учитывает регистр — «soft» определяется в слове «Software» несмотря на прописную букву «S».

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

"Software" –cmatch "soft"

Это выражение возвращает значение «Ложь», поскольку строка «soft» не совпадает с «Software» при сравнении с учетом регистра. Следует отметить, что также существует оператор imatch, явным образом определяющий отсутствие необходимости учета регистра, хотя это является стандартным поведением для оператора –imatch.

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

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

"Don" –match "D.n" (True)
"Dn" –match "D.n" (False)
"Don" –match "D?n" (True)
"Dn" –match "D?n" (True)

В первом примере, точка заменяет один символ, поэтому совпадение имеет значение «Истина». Во втором примере, точка не соответствует одному символу, поэтому результат — «Ложь». Вопросительный знак в третьем и четвертом выражении может соответствовать одному неизвестному символу или вообще не определять символы. Наконец, в четвертом примере, получен результат «Истина», поскольку и «D» и «n» были найдены без символа между ними. Таким образом, вопросительный знак можно рассматривать как заменяющий необязательный символ, так что совпадение будет по-прежнему иметь значение «Истина», если в этой позиции символ отсутствует.

Регулярное выражение также может использовать символы * и + как знаки повторения. Эти знаки должны следовать за каким-либо символом или символами. Знак * соответствует нулевому или большему числу указанных символов, в то время как + соответствует одному или более указанным символам. Вот несколько примеров.

"DoDon" –match "Do*n" (True)
"Dn" -match "Do*n" (True)
"DoDon" -match "Do+n" (True)
"Dn" -match "Do+n" (False)

Обратите внимание, что и *, и + соответствуют «Do», только не «o». Это происходит потому, что знаки повторения служат для сопоставления с рядом символов — не только одним символом.

А что если необходимо добиться совпадения с самими знаками точки, *, ?, или + ? В этом случае перед ними нужно просто указать обратную косую черту, которая служит управляющим символом для регулярного выражения:

"D.n" -match "D\.n" (True)

Обратите внимание, в Windows PowerShell используется другой управляющий символ (обратный апостроф), но это соответствует отраслевому синтаксису регулярных выражений.

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

Класс символов — расширенная форма подстановочных знаков, представляющая целую группу символов. Windows PowerShell распознает немного классов символов. Например:

  • \w соответствует любой букве и цифре.
  • \s соответствует любому символу разделителя, такому как символ табуляции, пробела и т.д.
  • \d соответствует цифровому символу.

Также существуют классы символов отрицания: \W соответствует любому символу, кроме буквы, \S соответствует любому символу, отличному от разделителя, и \D соответствует нецифровым символам. После этих классов можно указать * или +, чтобы определить возможность нескольких совпадений. Вот несколько примеров.

"Shell" -match "\w" (True)
"Shell" -match "\w*" (True)

Командлет месяца

Командлет Write-Debug очень помогает при записи объектов (например строк текста) в конвейер Debug. Однако использование этого командлета в оболочке может немного разочаровать, поскольку кажется, что он ничего не делает.

Смысл в том, что конвейер Debug закрывается по умолчанию — переменной $DebugPreference присваивается значение «SilentlyContinue». Задайте значение «Continue», и все отправляемые данные с командлетом Write-Debug появится в консоли в виде текста желтого цвета. Это отличная возможность трассировки кода для сценариев, которая позволяет следить за выполнением сложного сценария. Желтый цвет позволяет различать трассировку и нормальные результаты сценария, а отладочное сообщение можно отключить в любой момент без необходимости удаления всех выражений Write-Debug. Просто присвойте переменной $DebugPreference значение "SilentlyContinue" еще раз и отладочный текст будет отключен.

Несмотря на то, что оба выражения возвращают значение «Истина», они совпадают с абсолютно разными строками. К счастью, можно понять, что скрывает в себе оператор –match: при каждом совпадении, специальная переменная $matches заполняется результатами совпадения, т.е. независимо от того, с какими символами в строке, оператором было обнаружено совпадение с регулярным выражением. Переменная $matches возвращает собственные результаты до положительного совпадения оператора –match. На рис. 1 показана разница между двумя только что описанными выражениями. Как видите, оператор \w вызвал совпадение с «S» в слове «Shell», а повторяющийся оператор \w* — со всем словом.

Рис. 1 Возможность использования знака *

Рис. 1** Возможность использования знака * **(Щелкните изображение, чтобы увеличить его)

Группы символов, диапазоны и значения размера

Регулярное выражение может также включать группы или диапазоны символов, заключенные в квадратные скобки. Например [aeiou] означает, что любой из заключенных в скобки символов — a, e, i, o или u — является допустимым совпадением. Группа [a-zA-Z] означает, что любой из символов в диапазоне a-z или A-Z допустим (хотя, если использовать не учитывающий регистр оператор –match, будет достаточно указать только a-z или A-Z). Приведем пример:

"Jeff" -match "J[aeiou]ff" (True)
"Jeeeeeeeeeeff" -match "J[aeiou]ff" (False)

При помощи фигурных скобок можно также указать минимальное или максимальное число символов. {3} означает, что необходимо получить только три указанных символа, {3,} означает, что нужно от трех символов, и {3,4} что нужно от трех до четырех символов. Этот оператор дает отличную возможность создания регулярного выражения для IP-адресов:

"192.168.15.20" -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" (True)

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

"300.168.15.20" -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" (True)

Здесь определены пределы регулярного выражения. Хотя форматирование этой строки напоминает IP-адрес, очевидно, что это недопустимый IP-адрес. Регулярное выражение не может определить фактическую допустимость данных — возможно установить только правильность представления данных.

Исключение перемещения

Ошибки регулярного выражения определить не так просто. Например, ниже приведено регулярное выражение, проверяющее UNC-путь в формате \\Server2\Share:

"\\Server2\Share" -match "\\\\\w+\\\w+" (True)

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

"57\\Server2\Share" -match "\\\\\w+\\\w+" (True)

Второй пример, очевидно (по крайней мере, нам с вами), не может быть допустимым UNC-путем, но регулярное выражение сработало. Почему? Помните, что регулярное выражение по умолчанию будет «перемещаться» по строке. Это регулярное выражение просто выполняет поиск двойной косой черты, одной или нескольких букв и цифр, еще одной косой черты и других букв и цифр. Такой шаблон присутствует в строке вместе с другими цифрами в начале, что делает его недопустимым именем UNC. Теперь в регулярном выражении нужно указать поиск совпадения с начала строки — без перемещения. Это можно сделать следующим образом:

"57\\Server2\Share" -match "^\\\\\w+\\\w+" (False)

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

Подобным образом, символ $ можно использовать для обозначения конца строки. Однако этот символ не будет очень полезным в случае в UNC-путем, поскольку UNC-путь может содержать дополнительные сегменты, например \\Server2\Share\Folder\File. Но я уверен, во многих случаях может возникнуть необходимость указывать конец строки.

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

В Windows PowerShell, разделы справки about_regular_expressions включают основное описание синтаксиса языка регулярных выражений, но дополнительные сведения можно найти в Интернете. Например, на одном из моих любимых веб-узлов — www.RegExLib.com, находится бесплатная библиотека регулярных выражений, написанных для различных целей другими пользователям. Чтобы быстро найти подходящее регулярное выражение, можно воспользоваться поиском по ключевым словам «e-mail» или «UNC» — этого будет достаточно для начала. Если вам удастся создать отличное регулярное выражение, вы можете поделиться им, поместив его в библиотеку, чтобы им могли воспользоваться и другие пользователи.

Мне также нравится ресурс RegexBuddy (www.RegexBuddy.com). Здесь можно загрузить недорогой инструмент — графический редактор регулярных выражений. RegexBuddy упрощает сборку сложных выражений и тестирование регулярных выражений, что гарантирует должную обработку допустимых строк и отклонение недопустимых. Группа других разработчиков программного обеспечения также предлагает бесплатную, пробную и коммерческую версии редакторов регулярных выражений и инструментов тестирования, которые точно будут полезны пользователям.

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

Возможно, вам интересно узнать, в каких случаях могут понадобиться регулярные выражения. Предположим, вы считываете данные из CSV-файла и используете полученную информацию для создания новых пользователей в Active Directory®. Если CSV-файл создан другим пользователем, необходимо убедиться, что указанные в нем данные верны. Регулярные выражения идеально подходят для этого. Простое выражение типа \w+, например, поможет убедиться, что имена и фамилии пользователей не содержат каких-либо специальных символов или знаков, а более сложное выражение позволит установить соответствие адресов электронной почты принятому корпоративному стандарту. Например, можно использовать такое выражение:

"^[a-z]+\.[a-z]+@contoso.com$"

Это выражение определяет форму адреса электронной почты — don.jones@contoso.com, где имя и фамилия могут содержать только буквы и должны быть разделены точкой. Кстати, адреса электронной почты сложнее всего описывать в регулярном выражении. Если удастся ограничить поиск определенным корпоративным стандартом, то будет проще.

И не забывайте указывать начальные и конечные символы привязки (^ и $), чтобы гарантировать, что после сочетания «contoso.com» нет символов, и до него не указано имя пользователям, также представленное символами.

В принципе, использование подобного регулярного выражения в Windows PowerShell не представляет труда. Предположив, что переменная $email содержит адрес электронной почты, считываемый из CSV-файла, следующее выражение поможет проверить его допустимость:

$regex = "^[a-z]+\.[a-z]+@contoso.com$"
If ($email –notmatch $regex) {
  Write-Error "Invalid e-mail address $email" 
}

Кстати, в этом примере представлен новый оператор. Оператор -notmatch возвращает значение «Истина», если строка не соответствует указанному регулярному выражению. (Также существует оператор –cnotmatch для сравнения с учетом регистра.)

Можно еще очень много рассказать о регулярных выражениях — дополнительные символы классов, расширенные операции или даже оператор или два. А еще есть тип объекта [regex], поддерживаемый Windows PowerShell. Но в общем того, о чем мне удалось рассказать в этой ознакомительной статье, о синтаксисе регулярных выражений, должно быть достаточно для начала. Можете смело обращаться ко мне на форуме www.ScriptingAnswers.com, если крайне сложную головоломку в виде регулярного выражения решить не удается.

Дон Джонс (Don Jones) — пишущий редактор журнала TechNet Magazine и соавтор книги Windows PowerShell: TFM (издательство SAPIEN Press). Он обучает работе с Windows PowerShell (www.ScriptingTraining.com) и с ним можно связаться через веб-узел ScriptingAnswers.com.

© 2008 Корпорация Майкрософт и компания CMP Media, LLC. Все права защищены; полное или частичное воспроизведение без разрешения запрещено.