Select-String: Das grep von PowerShell

Juli 31, 2018 Sicherheit und Compliance, MOVEit

Wenn Sie PowerShell-Code schreiben und nach Text in einer einzelnen Zeichenkette oder in einer ganzen Textdatei suchen müssen, was benutzen Sie dann? Wenn Sie viel Linux benutzt haben, ist Ihnen wahrscheinlich das beliebte grep-Hilfsprogramm bekannt. Das grep-Hilfsprogramm erlaubt es Benutzern Text auf verschiedene Arten zu durchsuchen, aber dieses Hilfsprogramm gibt es in Windows nicht. Was können Sie also machen? Benutzen Sie das Select-String-Cmdlet.

Nehmen wir an, Sie haben eine lange Zeichenkette mit verschiedenen Angestelltennamen und –adressen. Unglücklicherweise ist diese Zeichenkette in keiner bekannten Struktur, also müssen Sie alle Namen durch Text-Parsing herausziehen. Wie können Sie das anstellen? Ich werde Ihnen das an folgendem Beispiel zeigen:

||Adam Bertram|| 2122 Acme Ct, Atlantic City, NJ
--||Joe Jonesy||-- 555 Lone St, Las Vegas, NV
==|Suzie Shoemaker|== 6783 Main St, Los Angelas, CA

Ich habe diese Zeichenkette einer Variablen $employees zugeordnet. Um nur die Angestelltennamen aus dieser Zeichenkette herauszuholen, versuche ich ersteinmal, die Syntax in Select-String richtig hinzubekommen. Dafür suche ich statisch mit dem Pattern-Parameter nach einem der Namen.

PS> $employees | Select-String -Pattern 'Adam Bertram'

||Adam Bertram|| 2122 Acme Ct, Atlantic City, NJ
--||Joe Jonesy||-- 555 Lone St, Las Vegas, NV
==|Suzie Shoemaker|== 6783 Main St, Los Angelas, CA

Beachten Sie, dass Select-String ein Ergebnis zurückgegeben hat, es hat also eine Übereinstimmung gefunden, sonst hätte es nichts herausgegeben. Aber es hat die gesamte Zeichenkette zurückgegeben. Warum passiert das? Select-String liest die gesamte Zeichenkette als eins. Wir müssen überlegen, wie jede dieser Zeilen in verschiedene Zeichenketten zerlegt werden kann. Da jeder Angestellten-Eintrag auf einer neuen Linie ist, kann ich die Kette bei dem new-line character (`n) auseinanderbrechen.

PS> $employees = $employees -split "`n"
PS> $employees | Select-String -Pattern 'Adam Bertram'

||Adam Bertram|| 2122 Acme Ct, Atlantic City, NJ

Sie sehen, dass nun nur eine Zeile zurückgegeben wird. Wir kommen dem Ergebnis näher! Als nächstes schaue ich, wie ich alle Zeilen mit Angestelltendaten zurückgeben kann. Dafür muss ich ein wiederkehrendes Muster finden, dass alle Zeilen gemeinsam haben. Es sieht so aus, als wäre jeder Angestelltenname durch das Zeichen | abgegrenzt. Man kann diese Wiederholung in dem Pattern-Parameter bei Select-String benutzen. Und da jeder Vor- und Nachname durch ein Leerzeichen abgegrenzt ist, kann man das hier auch anwenden.

Ich stelle dieses wiederkehrende Muster nun als regulären Ausdruck in dem Pattern-Parameter von Select-String dar.

PS> $employees | Select-String -Pattern '\|\w+ \w+\|'

||Adam Bertram|| 2122 Acme Ct, Atlantic City, NJ
--||Joe Jonesy||-- 555 Lone St, Las Vegas, NV
==|Suzie Shoemaker|== 6783 Main St, Los Angelas, CA

Select-String hat nun wieder jede Zeile durch Benutzung des regulären Ausdrucks (RegEx) wiedergegeben. Als nächstes muss man jeden einzelnen Angestelltennamen herausfiltern. Die Adressen lasse ich ersteinmal beiseite. Dafür verweise ich auf die Matches-Eigenschaft in jedem übereinstimmenden Objekt, das Select-String zurückgibt.

PS> $employees | Select-String -Pattern '\|\w+ \w+\|' | foreach {$_.Matches}

Groups   : {0}
Success  : True
Name     : 0
Captures : {0}
Index    : 1
Length   : 14
Value    : |Adam Bertram|

Groups   : {0}
Success  : True
Name     : 0
Captures : {0}
Index    : 3
Length   : 12
Value    : |Joe Jonesy|

Groups   : {0}
Success  : True
Name     : 0
Captures : {0}
Index    : 2
Length   : 17
Value    : |Suzie Shoemaker|

Wir sind fast am Ziel! Die Value-Eigenschaft enthält nun die Namen, die ich brauche, aber sie sind immer noch von den Pipe-Zeichen umgeben. Das ist klar, denn die Übereinstimmung des regulären Ausdrucks war der Angestelltenname einschließlich der Pipe-Zeichen. Man muss also die Pipe-Zeichen in der Suche einschließen, sie sollen aber nicht als Übereinstimmung zurückgegeben werden. Wie macht man das nun? Eine Möglichkeit ist, Gruppen im regulären Ausdruck zu verwenden. RegEx-Gruppen werden durch Klammern dargestellt, die die Übereinstimmung, die Sie zurückgeben möchten, umschließen. In diesem Fall umschließe ich also nur den RegEx-String, der den Vor- und Nachnamen der Angestellten darstellt und versuche es nocheinmal.

PS> $employees | Select-String -Pattern '\|(\w+ \w+)\|' | foreach {$_.Matches}

Groups   : {0, 1}
Success  : True
Name     : 0
Captures : {0}
Index    : 1
Length   : 14
Value    : |Adam Bertram|

Groups   : {0, 1}
Success  : True
Name     : 0
Captures : {0}
Index    : 3
Length   : 12
Value    : |Joe Jonesy|

Groups   : {0, 1}
Success  : True
Name     : 0
Captures : {0}
Index    : 2
Length   : 17
Value    : |Suzie Shoemaker|

Hmm..Value erscheint immer noch mit den Pipe-Zeichen. Aber die Group-Eigenschaft enthält nun 0,1 anstatt nur 0. Das bedeutet, dass Select-String eine Gruppe erkannt hat. Um diese Gruppe zu sehen, füge ich den Verweis wieder in unsere foreach loop hinzu. Da jede Gruppe ein Array ist, kann ich auf das 1 Element verweisen, indem ich es mit Klammern umgebe und dann auf die Value-Eigenschaft verweise.

PS> $employees | Select-String -Pattern '\|(\w+ \w+)\|' | foreach {$_.Matches.Groups[1].Value}
Adam Bertram
Joe Jonesy
Suzie Shoemaker

So habe ich nun jeden Angestelltennamen aus dem String gezogen! Select-String kann noch für viel mehr verwendet werden, schauen Sie also auch in der Hilfe unter: help Select-String -Detailed für einen vollständigen Überblick.

Tipp: Lesen Sie dazu auch das Whitepaper Automatisieren mit PowerShell oder laden Sie sich kostenfrei eine Testversion von MOVEit Automation, Software für die Automatisierung der Dateiübertragung, herunter.

Adam Bertram

Adam Bertram is a 20-year veteran of IT. He’s currently an automation engineer, blogger, independent consultant, freelance writer, author, and trainer. Adam focuses on DevOps, system management, and automation technologies as well as various cloud platforms. He is a Microsoft Cloud and Datacenter Management MVP and efficiency nerd that enjoys teaching others a better way to leverage automation.

Read next Messen der Neustart-Zeit mithilfe von PowerShell