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.