Select-String
Při našem posledním setkání, jsme si ukázali základy práce s regulárními výrazy. Věnovali jsme se operátoru –match a dnes tyto znalosti využijeme při práci s cmdletem Select-String.
Určitě jste někdy potřebovali prohledávat textové soubory na výskyt klíčového slova. Ve Windows lze použít příkaz findstr. Řekněme, že hledáte známý text lorem ipsum v souborem v určitém adresáři.
Jak je vidět i findstr používá regulární výraz (zde vyjádřeno pomocí parametru R). Zkusíme si předchozí příklad přepsat do PowerShellu.
PS C:\> Select-String 'lorem [ijk]psum' C:\Scripts\*.txt -List | Select Path
Path
----
C:\Scripts\lorem.txt
Vidíte, že v případě použití PowerShellu se findstr jeví o něco méně ukecaná varianta. Nesmíme ovšem zapomínat, že v PowerShellu pracujeme s objekty a výsledky můžeme dále zpracovávat v rouře. Více si povíme v další části článku.
Základní použití Select-String
Zkusíme získat aktuální IP adresu počítače z výpisu příkazu ipconfig.
PS C:\> ipconfig
Windows IP Configuration
Ethernet adapter Wireless Network Connection 3:
Connection-specific DNS Suffix . :
IP Address. . . . . . . . . . . . : 192.168.20.203
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.20.200
Potřebujeme získat řádku, která obsahuje text „IP Address“, Vzhledem k tomu, že ipconfig vrací pouze text:
PS C:\> ipconfig | gm
TypeName: System.String
... zkráceno
použijeme nejjednodušší variantu cmdletu Select-String.
PS C:\> ipconfig | Select-String 'ip address'
IP Address. . . . . . . . . . . . : 192.168.20.203
Vidíme, že Select-String je v základní podobě case insensitive a můžeme tedy hledaný výraz psát malými písmeny. Pokud bychom hledali case sensitive, musíme použít parametr CaseSensitive.
Jelikož jsme si říkali, že vše je objekt, pojďme se podívat na výsledek předchozí řádky z pohledu výsledného objektu:
PS C:\> ipconfig | Select-String 'ip address' | fl * -Force
IgnoreCase : True
LineNumber : 15
Line : IP Address. . . . . . . . . . . . : 192.168.20.203
Filename : InputStream
Path : InputStream
Pattern : ip address
Context :
Matches : {IP Address}
Hned na první řádce vidíme, že IgnoreCase je opravdu nastaveno na True. Dále jsou pro nás zajímavé vlastnosti Line, Pattern a Matches. Vzhledem k tomu, že vyhledáváme řádky obsahující určitý text, pro další zpracování nás bude nejvíc zajímat vlastnost Line.
PS C:\> $l = (ipconfig | Select-String 'ip address').Line.Trim()
PS C:\> $l
IP Address. . . . . . . . . . . . : 192.168.20.203
Zde jsme využili jednu z vynikajících vlastností PowerShellu. Pokud uzavřeme příkaz do závorek:
(ipconfig | Select-String 'ip address')
Chová se tento jako objekt a k jeho vlastnostem můžeme přistupovat pomocí tečkové notace. Zápis:
$l = (ipconfig | Select-String 'ip address').Line
Je tedy ekvivalentní zápisu
$l2 = ipconfig | Select-String 'ip address' | Select -Expand Line
Jelikož jsou na začátku řádku mezery, které se nám pro další zpracování nehodí, použili jsme metodu Trim(), abychom tyto mezery odstranili.
Pro získání IP adresy použijeme operátor –match:
PS C:\> $l -match '(?<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$'
True
PS C:\> $matches.ip
192.168.20.203
Vzor pro vyhledání IP adresy je jeden z nejjednodušších, který existuje. Není potřeba vymýšlet již vymyšlené. Vynikajícím zdrojem pro regulární výrazy je např. adresa http://regexlib.com.
Je vidět, že jednoduchým postupem jsme získali aktuální IP adresu počítače. Metod, pomocí kterých získáme lokální IP adresu, je veliké množství. Některé využívají WMI třídu Win32_NetworkAdapterConfiguration, jiné používají .NET třídu System.Net.Dns. Je čistě na vás, jakou metodu byste v tomto případě preferovali. Předem upozorňuji na to, že naznačený postup bude fungovat, pokud máte IP adresu přiřazenou pouze pro jeden adaptér.
Vzhledem k tomu, že jsme pro vyhledávání adresy nepoužili žádný regulární výraz, stačilo by nám použít parametr SimpleMatch.
PS C:\> ipconfig | Select-String 'ip address' -SimpleMatch
IP Address. . . . . . . . . . . . : 192.168.20.203
Pokud bychom například chtěli získat všechny řádky, které obsahují IP adresu, SimpleMatch bychom nemohli použít. Select-String funguje standardně tak, že regulární výrazy používá.
PS C:\> ipconfig | Select-String '(?<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
IP Address. . . . . . . . . . . . : 192.168.20.203
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.20.200
Dále bych výstup mohli zpracovat libovolným způsobem na principu první ukázky.
Prohledávání logů
Dále budeme pracovat s logem WindowsUpdate.log. Co třeba zobrazit si verzi klienta Windows Update. Můžeme se podívat, jestli se verze klienta v únoru (2012-02) změnila
Get-Content c:\WINDOWS\WindowsUpdate.log | Select-String '^2012-02.*WU client version'
2012-02-12 20:51:52:796 1044 c50 Agent * WU client version 7.4.7600.226
2012-02-13 21:25:55:343 1040 6a4 Agent * WU client version 7.4.7600.226
2012-02-17 22:34:53:046 1040 cdc Agent * WU client version 7.4.7600.226
2012-02-18 11:21:26:843 1040 14c Agent * WU client version 7.4.7600.226
2012-02-19 11:46:17:250 1232 7b4 Agent * WU client version 7.4.7600.226
2012-02-19 15:48:13:156 1244 780 Agent * WU client version 7.4.7600.226
2012-02-19 21:11:23:406 1048 ab0 Agent * WU client version 7.4.7600.226
...zkráceno
Select-String umožňuje zobrazit i kontext, ve kterém se hledaný řetězec vyskytuje. Struktura log souboru v okolí hledaného textu je následující:
2012-02-24 08:12:07:593 1068 6d0 Misc =========== Logging initialized (build: 7.4.7600.226, tz: +0100) ===========
2012-02-24 08:12:07:640 1068 6d0 Misc = Process: C:\WINDOWS\System32\svchost.exe
2012-02-24 08:12:07:640 1068 6d0 Misc = Module: C:\WINDOWS\system32\wuaueng.dll
2012-02-24 08:12:07:578 1068 6d0 Service *************
2012-02-24 08:12:07:640 1068 6d0 Service ** START ** Service: Service startup
2012-02-24 08:12:07:640 1068 6d0 Service *********
2012-02-24 08:12:08:015 1068 6d0 Agent * WU client version 7.4.7600.226
2012-02-24 08:12:08:015 1068 6d0 Agent * Base directory: C:\WINDOWS\SoftwareDistribution
2012-02-24 08:12:08:031 1068 6d0 Agent * Access type: No proxy
2012-02-24 08:12:08:171 1068 6d0 Agent * Network state: Connected
2012-02-24 08:12:55:906 1068 6d0 Agent *********** Agent: Initializing Windows Update Agent ***********
2012-02-24 08:12:55:921 1068 6d0 Agent *********** Agent: Initializing global settings cache ***********
2012-02-24 08:12:55:921 1068 6d0 Agent * WSUS server: <NULL>
2012-02-24 08:12:55:921 1068 6d0 Agent * WSUS status server: <NULL>
2012-02-24 08:12:55:921 1068 6d0 Agent * Target group: (Unassigned Computers)
2012-02-24 08:12:55:921 1068 6d0 Agent * Windows Update access disabled: No
2012-02-24 08:12:56:031 1068 6d0 Agent * Found 1 persisted download calls to restore
Pokud chceme zobrazit stejný počet řádek před a za hledaným textem, použijeme parametr Context s jedním argumentem:
PS C:\> Get-Content c:\WINDOWS\WindowsUpdate.log | Select-String '^2012-02.*WU client version' -Context 2
2012-02-12 20:51:52:531 1044 c50 Service ** START ** Service: Service startup
2012-02-12 20:51:52:578 1044 c50 Service *********
> 2012-02-12 20:51:52:796 1044 c50 Agent * WU client version 7.4.7600.226
2012-02-12 20:51:53:140 1044 c50 Agent * Base directory: C:\WINDOWS\SoftwareDistribution
2012-02-12 20:51:53:140 1044 c50 Agent * Access type: No proxy
2012-02-13 21:25:55:328 1040 6a4 Service ** START ** Service: Service startup
2012-02-13 21:25:55:328 1040 6a4 Service *********
> 2012-02-13 21:25:55:343 1040 6a4 Agent * WU client version 7.4.7600.226
2012-02-13 21:25:55:343 1040 6a4 Agent * Base directory: C:\WINDOWS\SoftwareDistribution
2012-02-13 21:25:55:343 1040 6a4 Agent * Access type: No proxy
...zkráceno
Znak > ukazuje řádku s hledaným textem a vidíme dvě řádky před a za ním. V případě, že chceme vidět specifický počet řádek před/za textem, použijeme dva argumenty:
PS C:\> Get-Content c:\WINDOWS\WindowsUpdate.log | Select-String '^2012-02.*WU client version' -Context 0,3
> 2012-02-12 20:51:52:796 1044 c50 Agent * WU client version 7.4.7600.226
2012-02-12 20:51:53:140 1044 c50 Agent * Base directory: C:\WINDOWS\SoftwareDistribution
2012-02-12 20:51:53:140 1044 c50 Agent * Access type: No proxy
2012-02-12 20:51:53:406 1044 c50 Agent * Network state: Connected
> 2012-02-13 21:25:55:343 1040 6a4 Agent * WU client version 7.4.7600.226
2012-02-13 21:25:55:343 1040 6a4 Agent * Base directory: C:\WINDOWS\SoftwareDistribution
2012-02-13 21:25:55:343 1040 6a4 Agent * Access type: No proxy
2012-02-13 21:25:55:343 1040 6a4 Agent * Network state: Connected
Znak > nám opět ukazuje hledanou řádku a vidíme, že kontext se zobrazuje pouze za ní (tři následující řádky).
Prohledávání více souborů
Velmi častým případem bude prohledávání více souborů. Můžeme si například zobrazit všechny výskyty textu „failed“ v log souborech pro jednotlivé záplaty.
PS C:\> ls c:\Temp\kb*.log | Select-String 'failed'
Temp\KB2412687.log:5:2.250: Failed To Enable SE_SHUTDOWN_PRIVILEGE
Temp\KB2412687.log:7:2.250: In Function GetReleaseSet, line 1240, RegQueryValueEx failed with error 0x2
Temp\KB2412687.log:46:3.266: In Function GetReleaseSet, line 1240, RegQueryValueEx failed with error 0x2
Temp\KB2412687.log:85:8.375: LoadFileQueues: UpdSpGetSourceFileLocation for halmacpi.dll failed: 0xe0000102
Temp\KB2485663.log:5:2.891: Failed To Enable SE_SHUTDOWN_PRIVILEGE
Temp\KB2485663.log:7:2.891: In Function GetReleaseSet, line 1240, RegQueryValueEx failed with error 0x2
Temp\KB2485663.log:78:3.375: In Function GetReleaseSet, line 1240, RegQueryValueEx failed with error 0x2
Temp\KB2497640-IE8.log:5:2.640: Failed To Enable SE_SHUTDOWN_PRIVILEGE
Temp\KB2497640-IE8.log:7:2.734: In Function GetReleaseSet, line 1240, RegQueryValueEx failed with error 0x2
Temp\KB2497640-IE8.log:49:3.594: In Function GetReleaseSet, line 1240, RegQueryValueEx failed with error 0x2
Temp\KB2503658.log:6:2.046: In Function GetReleaseSet, line 1240, RegQueryValueEx failed with error 0x2
Temp\KB2503658.log:7:2.484: DoInstallation: CleanPFR failed: 0x2
...zkráceno
Vidíte, že Select-String vypíše jednotlivé řádky obsahující hledaný text (včetně čísla řádky) a jméno souboru, ve kterém se text vyskytuje. Pokud bychom chtěli vidět pouze první výskyt hledaného textu v souborech, použili bychom parametr List.
PS C:\> ls c:\Temp\*.log | Select-String 'failed' -List
Temp\KB2412687.log:5:2.250: Failed To Enable SE_SHUTDOWN_PRIVILEGE
Temp\KB2485663.log:5:2.891: Failed To Enable SE_SHUTDOWN_PRIVILEGE
Temp\KB2497640-IE8.log:5:2.640: Failed To Enable SE_SHUTDOWN_PRIVILEGE
Temp\KB2503658.log:6:2.046: In Function GetReleaseSet, line 1240, RegQueryValueEx failed with error 0x2
...zkráceno
Select-String je vynikající cmdlet pro práci s texty. V některých případech je lepší využít operátor –match, ale pro hledání informací ve velkém množství souborů (např. logy – viz příklad) je Select-String ideální variantou.