Seriál Windows PowerShell – Zpracování textu (část 51.)

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.