Всем привет.
Часто можно встретить вопросы связанные не столько с самим 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. Неплохо, неправда ли? Так вот, по сравнению с возможностями регулярных выражения - это пустяки.
https://nyukers.blogspot.com/2017/12/logparser.html
Супер Select-String.
Не буду сегодня переписывать все подробности применения регулярки в PowerShell. Остановлюсь лишь на командлете PowerShell, который использует регулярные выражения. Таким командлетом является Select-String. Он используется для поиска строк совпадающих с регулярным выражением. Строки для отбора можно передать из массива строк, например:
PS C:\> $lines = Get-Content C:\Windows\setupact.log
PS C:\> $lines | Select-String "error"
dispci.dll: DispCIOpenDxgKrnlAndDisableNewReferences: D3DKMTOpenAdapterFromDeviceName failed with error 0xc000007a.
[10/24/2009 20:47.16.192] WudfCoInstaller: Final status: error(0) The operation completed successfully.
[10/26/2009 14:45.08.912] WudfCoInstaller: Final status: error(0) The operation completed successfully.
[10/27/2009 18:24.13.032] WudfCoInstaller: Final status: error(0) The operation completed successfully.
[10/27/2009 18:24.14.421] WudfCoInstaller: Final status: error(0) The operation completed successfully.
[11/02/2009 11:32.22.880] WudfCoInstaller: Final status: error(0) The operation completed successfully.
[11/13/2009 15:16.16.837] WudfCoInstaller: Final status: error(0) The operation completed successfully.
Еще можно указывать файлы для проверки содержащихся в них строк, просто указав их путь, или маску (с помощью обычных подстановочных знаков). Так в следующем примере я делаю поиск строки error:<code> во всех файлах <code>*.log в папке c:\Windows:
PS C:\> Select-String "error:" C:\Windows\*.log
TSSysprep.log:7:sysprep.cpp(314)ERROR: ResetTSPublicPrivateKeys() FAILED: 2
WindowsUpdate.log:2663:2009-10-31 15:11:26:983 896 13fc PT WARNING: PTError: 0x80072ee2
WindowsUpdate.log:3926:2009-11-01 19:09:14:748 896 548 PT WARNING: PTError: 0x8024402c
WindowsUpdate.log:3930:2009-11-01 19:09:14:749 896 548 PT WARNING: PTError: 0x8024402c
WindowsUpdate.log:3941:2009-11-01 19:09:28:778 896 548 PT WARNING: PTError: 0x8024402c
WindowsUpdate.log:3945:2009-11-01 19:09:28:778 896 548 PT WARNING: PTError: 0x8024402c
WindowsUpdate.log:3956:2009-11-01 19:09:42:808 896 548 PT WARNING: PTError: 0x8024402c
WindowsUpdate.log:3960:2009-11-01 19:09:42:808 896 548 PT WARNING: PTError: 0x8024402c
Select-String отличается от конструкции where {$_ -match "error:"} тем что выводит не просто совпадения строк, а полноценные объекты содержащие дополнительную информацию. В данном случае были выведены не только совпавшие строки, но и файлы в которых они были найдены (TSSysprep.log и WindowsUpdate.log), и номера строк. Полный список доступных свойств можно посмотреть следующей командой:
PS C:\> Select-String "error:" C:\Windows\*.log |
>> Get-Member -MemberType property
TypeName: Microsoft.PowerShell.Commands.MatchInfo
Name MemberType Definition
---- ---------- ----------
Context Property Microsoft.PowerShell.Commands.MatchInfoContext Context {get;set;}
Filename Property System.String Filename {get;}
IgnoreCase Property System.Boolean IgnoreCase {get;set;}
Line Property System.String Line {get;set;}
LineNumber Property System.Int32 LineNumber {get;set;}
Matches Property System.Text.RegularExpressions.Match[] Matches {get;set;}
Path Property System.String Path {get;set;}
Pattern Property System.String Pattern {get;set;}
Давайте например выведем только имена файлов и номера совпавших строк:
PS C:\> Select-String "error:" C:\Windows\*.log |
>> Format-Table Path, LineNumber -AutoSize
Path LineNumber
---- ----------
C:\Windows\TSSysprep.log 7
C:\Windows\WindowsUpdate.log 2663
C:\Windows\WindowsUpdate.log 3926
C:\Windows\WindowsUpdate.log 3930
C:\Windows\WindowsUpdate.log 3941
C:\Windows\WindowsUpdate.log 3945
C:\Windows\WindowsUpdate.log 3956
C:\Windows\WindowsUpdate.log 3960
Если весь этот «объектный мусор» вам не нужен, вы можете получить только строки, следующей командой:
PS C:\> Select-String "error:" C:\Windows\*.log |
>> Select-Object -ExpandProperty line
sysprep.cpp(314)ERROR: ResetTSPublicPrivateKeys() FAILED: 2
2009-10-31 15:11:26:983 896 13fc PT WARNING: PTError: 0x80072ee2
2009-11-01 19:09:14:748 896 548 PT WARNING: PTError: 0x8024402c
2009-11-01 19:09:14:749 896 548 PT WARNING: PTError: 0x8024402c
2009-11-01 19:09:28:778 896 548 PT WARNING: PTError: 0x8024402c
2009-11-01 19:09:28:778 896 548 PT WARNING: PTError: 0x8024402c
2009-11-01 19:09:42:808 896 548 PT WARNING: PTError: 0x8024402c
2009-11-01 19:09:42:808 896 548 PT WARNING: PTError: 0x8024402c
У Select-String есть и несколько дополнительных возможностей. Так если вам не интересно знать какие строки совпали, а лишь необходимо выяснить были ли совпадения вообще, воспользуйтесь ключом -Quiet:
PS C:\> netsh advfirewall firewall show rule "Remote Desktop (TCP-In)" |
>> select-string "Enabled:\s+Yes" -Quiet
True
Эта команда проверяет, содержится ли в выводе netsh строка совпадающая с Enabled:\s+Yes и если содержится, то выводит значение $True. Разумеется тут тоже можно указывать напрямую имя файла или несколько с помощью подстановочных символов, тогда True будет выдано в случае если хотя бы один из файлов содержит указанную строку.
Параметр -List говорит Select-String что нужно найти лишь по одному совпадению на каждый файл. Это может быть полезно если вам надо найти все файлы содержащие определенную строку:
PS C:\> Select-String "error:" C:\Windows\*.log -List |
>> select -ExpandProperty path
C:\Windows\TSSysprep.log
C:\Windows\WindowsUpdate.log
В PowerShell 2.0 у Select-String появился еще один очень полезный ключ -Context. Который мне особенно нравится по аналогии с LogParser. Он позволяет вывести не только совпавшую строку, но еще и указанное количество строк до неё и после неё. В следующем примере выводится 3 строки предшествующих совпадению и одна после него:
PS C:\> Select-String "error:" C:\Windows\TSSysprep.log -Context 3,1
Windows\TSSysprep.log:4:*******Version:Major=6, Minor=1, Build=7600, PlatForm=2, CSDVer=, Free
Windows\TSSysprep.log:5:
Windows\TSSysprep.log:6:sysprep.cpp(309)Entering RCMSysPrepRestore
> Windows\TSSysprep.log:7:sysprep.cpp(314)ERROR: ResetTSPublicPrivateKeys() FAILED: 2
Windows\TSSysprep.log:8:sysprep.cpp(316)Leaving RCMSysPrepRestore
Непосредственно совпавшая строка помечается с помощью символа > в начале строки. Если указать в качестве аргумента не массив из двух элементов, а просто число, то будет выведено указанное количество строк с обоих сторон от совпадения:
PS C:\> netsh advfirewall firewall show rule "Remote Desktop (TCP-In)" |
>> select-string "Enabled:" -Context 2
Rule Name: Remote Desktop (TCP-In)
----------------------------------------------------------------------
> Enabled: Yes
Direction: In
Profiles: Domain,Private,Public
В Select-String тоже можно использовать группы захвата, хотя получить их содержимое несколько сложнее. Дело в том что тут не используется специальная переменная $Matches, а вместо неё результаты совпадения, в виде объекта System.Text.RegularExpressions.Match помещаются в свойство Matches результирующего объекта. Подробнее устройство этого объекта мы рассмотрим позднее, когда будем изучать класс [Regex], а пока я просто покажу как же можно получить например значение первой группы захвата:
PS C:\> Select-String "error: (\S+)" C:\Windows\*.log |
>> Format-table path,linenumber,{$_.Matches[0].groups[1].value}
Path LineNumber $_.Matches[0].groups[1].value
---- ---------- -----------------------------
C:\Windows\TSSysprep.log 7 ResetTSPublicPrivateKeys()
C:\Windows\WindowsUpdate.log 2663 0x80072ee2
C:\Windows\WindowsUpdate.log 3926 0x8024402c
C:\Windows\WindowsUpdate.log 3930 0x8024402c
C:\Windows\WindowsUpdate.log 3941 0x8024402c
C:\Windows\WindowsUpdate.log 3945 0x8024402c
C:\Windows\WindowsUpdate.log 3956 0x8024402c
C:\Windows\WindowsUpdate.log 3960 0x8024402c
Другие полезные параметры командлета на которые стоит обратить внимание, это -CaseSensetive, -Encoding и -NotMatch.
Успехов.
No comments:
Post a Comment
А что вы думаете по этому поводу?