V dnešním textu se podíváme na zpracování textu a na použití PowerShellu při potřebě „rychlé akce“. Jsem právě v Redmodu v campusu Microsoftu na letošním MVP Summitu. Stejně jako každá jiná konference, i tato je nabita přednáškami od pondělí až do pátku. Oficiální program začíná již v neděli a není proto divu, že po několika dnech už tělo začíná protestovat.
Důležité je vybrat si správné prezentace. Mimo těch, které se týkají mé oblasti, je možné sáhnout do podobných oborů, což využívám v případě Microsoft Azure. I když mám již svůj program nahrán v telefonu, občas se hodí kouknout se, jestli se nezměnilo téma nějaké prezentace nebo se prostě neobjevila nějaká zajímavější.
Všechny prezentace máme přehledně na webu, takže není problém si je stáhnout do textového souboru, např. pomocí nám již známého Invoke-WebRequest. Nebo čistým copy-paste, jak jsem to udělal já J Vyhledávání v tomto souboru bylo ovšem trochu složitější a tak jsem otevřel konzoli a snažil se trochu si ulehčit práci.
Pozn.: Témata jednotlivých prezentací jsou bohužel NDA, proto jsem pro účely tohoto článku upravil některé názvy.
Pozn. 2: Na dnešním příkladě bych chtěl ukázat, že pro rychlé akce náhodného typu můžete PowerShell využít k klidným svědomím. Celé následně popsané řešení bylo otázkou zhruba pěti minut při přejezdu mezi budovami, kde se přednášky konaly.
Nejprve jsem si čistě vylistoval obsah souboru pomocí Get-Content a následně zjistil, že v Notepadu je hledání samozřejmě pohodlnější. Nicméně jsem přešel na těžší váhu: Select-String. Nejprve se podíváme na řádky s aktuálním dnem:
PS C:\> Select-String $path -Pattern thur
Users\Makovec\Documents\fri.txt:3:Thursday, November 6, 09:00 - 10:00, Building/Room: MS Campus 33/room1
Users\Makovec\Documents\fri.txt:13:Thursday, November 6, 09:00 - 09:30, Building/Room: MS Campus 33/room2
Users\Makovec\Documents\fri.txt:23:Thursday, November 6, 09:00 - 10:30, Building/Room: MS Campus 34/room3
Users\Makovec\Documents\fri.txt:32:Thursday, November 6, 13:30 - 15:00, Building/Room: MS Campus 33/room3
Select-String hledá text na základě regulárního výrazu. V našem případě to nebudeme potřebovat, ale nebudeme příkaz měnit. Pokud byste chtěli čistě textové hledání, použijte switch SimpleMatch.
Dostali jsme zajímavý výpis, ale moc užitečný nám není. Zkusme přidat parametr Context. Tímto parametrem říkáme kolik řádek před a po aktuálním nalezeném textu chceme zobrazit.
PS C:\> Select-String $path -Pattern thur -Context 2,0
Users\Makovec\Documents\fri.txt:1:.NET a jeho vyuziti pri uprave zahradky: Introduction
Users\Makovec\Documents\fri.txt:2:(AB21) 36 spots of 88 are available for this session.
> Users\Makovec\Documents\fri.txt:3:Thursday, November 6, 09:00 - 10:00, Building/Room: MS Campus/room1
Users\Makovec\Documents\fri.txt:11:Access for corps
Users\Makovec\Documents\fri.txt:12:(AB13) 111 spots of 144 are available for this session.
> Users\Makovec\Documents\fri.txt:13:Thursday, November 6, 09:00 - 09:30, Building/Room: MS Campus/room2
Users\Makovec\Documents\fri.txt:21:SQL Server 2005 - novinky
Users\Makovec\Documents\fri.txt:22:(SD12) 10 spots of 20 are available for this session.
> Users\Makovec\Documents\fri.txt:23:Thursday, November 6, 09:00 - 10:30, Building/Room: MS Campus/room3
Users\Makovec\Documents\fri.txt:30:Access & Excel
Ha – vypadá to, že máme výsledek. Tedy, téměř. Samozřejmě že stále máme text, který nevypadá moc pěkně. Zkusíme ho trošku vylepšit.
Zde se nám hodí náš starý kamarád – Get-Member.
PS C:\> Select-String $path -Pattern thur -Context 2,0 | gm
TypeName: Microsoft.PowerShell.Commands.MatchInfo
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
RelativePath Method string RelativePath(string directory)
ToString Method string ToString(), string ToString(string directory)
Context Property Microsoft.PowerShell.Commands.MatchInfoContext Context {get;set;}
Filename Property string Filename {get;}
IgnoreCase Property bool IgnoreCase {get;set;}
Line Property string Line {get;set;}
LineNumber Property int LineNumber {get;set;}
Matches Property System.Text.RegularExpressions.Match[] Matches {get;set;}
Path Property string Path {get;set;}
Pattern Property string Pattern {get;set;}
Docela zajímavě vypadá vlastnost Context.
PS C:\> Select-String $path -Pattern thur -Context 2,0 | select context
Context
-------
Microsoft.PowerShell.Commands.MatchInfoContext
Microsoft.PowerShell.Commands.MatchInfoContext
Microsoft.PowerShell.Commands.MatchInfoContext
Microsoft.PowerShell.Commands.MatchInfoContext
Users\Makovec\Documents\fri.txt:31:(CD42) 125 spots of 144 are available for this session.
> Users\Makovec\Documents\fri.txt:32:Thursday, November 6, 13:30 - 15:00, Building/Room: MS Campus/room3
Což není úplně přesně to, co jsme chtěli. Nicméně víme, že pro podívání se dovnitř výsledného objektu pomocí parametru ExpandProperty.
C:\> Select-String $path -Pattern thur -Context 2,0 | select -expand context
PreContext PostContext DisplayPreContext DisplayPostContext
---------- ----------- ----------------- ------------------
{.NET a jeho vyuziti pri u... {} {.NET a jeho vyuziti pri u... {}
{Access for corps, (AB13) ... {} {Access for corps, (AB13) ... {}
{SQL Server 2005 - novinky... {} {SQL Server 2005 - novinky... {}
{Access & Excel, (CD42) ... {} {Access & Excel, (CD42) ... {}
PreContext se jeví jako zajímavá možnost.
PS C:\> Select-String $path -Pattern thur -Context 2,0 | select -ExpandProperty Context | Select -Expand PreContext
.NET a jeho vyuziti pri uprave zahradky: Introduction
(AB21) 36 spots of 88 are available for this session.
Access for corps
(AB13) 111 spots of 144 are available for this session.
SQL Server 2005 - novinky
(SD12) 10 spots of 20 are available for this session.
Access & Excel
(CD42) 125 spots of 144 are available for this session.
Nyní již máme zhruba to, co jsme chtěli na začátku. Jenom by bylo dobré vědět, od kolika hodin se přednášky konají J Pohledem na výstup Get-Member zjistíme, kde by se mohla vyskytnout požadovaná informace a pomocí Format-Listsi můžeme naši teorii ověřit.
PS C:\> Select-String $path -Pattern thur -Context 2,0 | fl *
IgnoreCase : True
LineNumber : 3
Line : Thursday, November 6, 09:00 - 10:00, Building/Room: MS Campus/room1
Filename : fri.txt
Path : C:\Users\Makovec\Documents\fri.txt
Pattern : thur
Context : Microsoft.PowerShell.Commands.MatchInfoContext
Matches : {Thur}
Vidíme, že potřebné info je ve vlastnosti Line. Trošku si změníme kód, protože budeme skládat informace z několika vlastností dohromady.
PS C:\> foreach($r in (Select-String $path -Pattern thur -Context 2,0)) { $r.Context.PreContext }
Výsledek je pořád stejný, můžeme pokročit dále. V tomto okamžiku bych asi normálně přepl do ISE, ale vzhledem k tomu, že jsem si s sebou bral pouze můj Surface RT, kde ISE není, zůstal jsem nadále v konzoli a celé řešení nakonec tvořil dále jako one-liner.
PS C:\> foreach($r in (Select-String $path -Pattern thur -Context 2,0)) { "$($r.line)`n$($r.Context.PreContext[0])" }
Thursday, November 6, 09:00 - 10:00, Building/Room: MS Campus/room1
.NET a jeho vyuziti pri uprave zahradky: Introduction
Thursday, November 6, 09:00 - 09:30, Building/Room: MS Campus/room2
Access for corps
Thursday, November 6, 09:00 - 10:30, Building/Room: MS Campus/room3
SQL Server 2005 - novinky
Thursday, November 6, 13:30 - 15:00, Building/Room: MS Campus/room3
Access & Excel
Nyní vidíme datum i jméno přednášky. Vzhledem k tomu, že výsledek se mi ještě tolik nelíbil, upravil jsem jej ještě trochu. Dále uvádím pouze část, která zobrazuje informace a pro přehlednost jsem ji rozdělil na jednotlivé řádky:
$($r.line.Substring(22,13)) | Z vyhledaného textu vybere část s časem prezentace |
$($r.context.precontext[0]) | Jméno prezentace |
$(" "*13) | Na nové řádce vypíše 13x mezeru. Tuto techniku můžete využít v různých scénářích |
$($r.context.precontext[1].Substring(0,7)) | Identifikační kód prezentace |
$(($r.line -split ':')[3].trim()) | Místo, kde se prezentace bude konat |
Nyní máme všechny potřebné informace pohromadě. Proto je spojíme do jednoho příkazu.
PS C:\> foreach($r in (Select-String -Path $path -Pattern 'thu' -Context 2,0)) { "$($r.line.Substring(22,13)): $($r.context.precontext[0])`n $(" "*13) ($r.context.precontext[1].Substring(0,7)) at $(($r.line -split ':')[3].trim())`n"}
09:00 - 10:00: .NET a jeho vyuziti pri uprave zahradky: Introduction
(AB21) at MS Campus/room1
09:00 - 09:30: Access for corps
(AB13) at MS Campus/room2
09:00 - 10:30: SQL Server 2005 - novinky
(SD12) at MS Campus/room3
13:30 - 15:00: Access & Excel
(CD42) at MS Campus/room3
Pro jednodušší čtení jsem si ještě příkaz upravil tak, aby mi zobrazoval informace pouze za určitý časový interval, nicméně to jsem již dodělával po zmiňované cestě autobusem a věřím, že to pro vás bude případně pěkné cvičení.
Když tak koukám na výsledek, hodilo by se, kdyby informace nebyly pouze textové, ale ve formě objektu. V návazném díle si ukážeme, jak toho dosáhnout s daleko menším úsilím pomocí cmdletu nově dostupného v PowerShellu v5.