Nicht nur Einsteiger in Programmier- und Script-Sprachen, sondern auch viele "gestandene Administratoren" schütteln nur verzweifelt den Kopf, wenn die Rede auf die regulären Ausdrücke kommt. Zu verwirrend scheinen die Hieroglyphen, die schon altgediente Unix-Administratoren einsetzten, um ihren Anwendern zu demonstrieren, wie mächtig das berüchtigte grep-Kommando sein kann - wenn es richtig einsetzt wird. Aber nicht nur bei den Unix-/Linux-Shell-Programmen, sondern auch bei der modernen PowerShell, die fester Bestandteil aller aktuellen Windows-Client- und Server-Betriebssysteme ist, kann mit den regulären Ausdrücken effizient gearbeitet werden. Die PowerShell unterstützt dabei den gesamten Umfang der regulären Ausdrücke, der vom .NET-Framework bereitgestellt wird.
Wir erläutern in diesem Beitrag die Grundlagen und zeigen anhand einer ganzen Reihe von Beispielen, dass diese kryptischen Zeichen sehr gut in der Praxis eingesetzt werden können.
Das Arbeitsfeld der regulären Ausdrücke: Vergleichen und Ersetzen
Die regulären Ausdrücke kommen hauptsächlich dann zum Einsatz, wenn es darum geht, Vergleiche durchzuführen oder etwa auch Werte und Zeichen zu ersetzen. Neben den Operatoren zum direkten Vergleich von Werten wie "-eq" (equal) oder -gt (greater than) gehören der sogenannte Ähnlichkeitsoperator "-like" (und "-unlike") sowie der Ersetzungsoperator "-replace" und die Übereinstimmungsoperatoren "-match" und "-unmatch" zur Kategorie der Vergleichsoperatoren.
Von ihnen können sowohl "-replace" als auch "-match" und "-unmatch" mit den regulären Zeichen umgehen, während der "-like"-Operator nur mit dem Wildcard-Zeichen "*" zusammenarbeitet, das für eine beliebige Anzahl von Zeichen steht. So bringen dann auch die folgenden beiden Aufrufe die genau gleiche Ausgabe auf den Bildschirm:
Get-Service | where {$_.status -like "running"}
Get-Service | where {$_.status -match "running"}
Beide Aufrufe zeigen alle auf dem jeweiligen System aktiven (running) Prozesse an. Der Unterschied liegt darin, dass die Vergleiche beim Einsatz von "-match" und den regulären Zeichen weitaus genauer und zielgerichteter durchgeführt werden können. Ein Hinweis ist in diesem Zusammenhang noch wichtig: Diese Art der Abfrage ist nicht Case-Sensitive - das bedeutet, dass hierbei nicht zwischen Groß- und Kleinschreibung unterschieden wird und beide Aufrufe sowohl Prozesse finden, die als "running" gekennzeichnet sind, als auch solche, die beispielsweise als "Running" ausgegeben werden.
Metazeichen, Suchmuster und was sie bewirken
Aber dazu ist es zunächst einmal wichtig, kurz zu erläutern, was sich hinter dem Begriff reguläre Ausdrücke eigentlich verbirgt. Grundsätzlich handelt es sich dabei um ein Muster (eine Zeichenfolge), die Daten beschreibt. So repräsentiert dann ein solcher Ausdruck immer eine bestimmte Art von Daten im Suchmuster.
Dazu werden auch sogenannte Metazeichen eingesetzt. Die wichtigsten Zeichen - die auch von der PowerShell verwendet werden - sind die folgenden:
., ^, $, [], { }, *, ?, \
wobei es sich hierbei keinesfalls um eine vollständige Aufzählung der bei den regulären Ausdrücken zum Einsatz kommenden Metazeichen handelt.
Wenn es sich also bei einem regulären Ausdruck lediglich um ein Muster handelt, was macht die PowerShell dann mit diesem Muster? Sie benutzt es dazu, es mit den ihr übergebenen Daten zu vergleichen. Hier ein einfaches Beispiel:
PS C: \> $Eingabe=‘TecChannel‘
Bei dem Objekt, das wir hier der Variablen "Eingabe" zugewiesen haben, handelt es sich eindeutig um eine Zeichenkette (String). Die regulären Ausdrücke in der PowerShell arbeiten mit Zeichenklassen, wie sie auch im Microsoft-.NET-Framework 3.5 zur Verfügung stehen. Um mithilfe des match-Operators festzustellen, ob es sich bei einem Objekt beispielsweise um ein "Wortzeichen" handelt, kann der folgende Aufruf eingesetzt werden:
PS C:\> $Eingabe -match "\w"True
Als "Wortzeichen" wird hierbei die Menge der Zeichen von a-z sowie A-Z und 0-9 angesehen. Wichtig hierbei: Soll dahingehend verglichen werden, ob es sich um ein solches Wortzeichen handelt, dann muss hinter dem Escape-Zeichen "\" ein kleines "w" folgen - an dieser Stelle wird sehr wohl zwischen Groß- und Kleinschreibung unterschieden. Kommt ein großes "W" zum Einsatz, so wird auf "Nicht-Wortzeichen" verglichen. Ein Beispiel dafür wäre ein Leerzeichen im String. So wird dann der folgende Aufruf:
PS C:\> $Eingabe -match "\W"False
logischerweise als unrichtig (False) angezeigt. Die PowerShell vergleicht bei diesem Aufruf, wie wir ihn hier angegeben haben, das zu untersuchende Muster so lange, bis die Bedingung zutrifft. Dies lässt sich schön durch das automatisch angelegte und bei einem Aufruf von -match auch automatisch gefüllte Objekt "$matches" zeigen. Das sieht dann bei unserem ersten Aufruf folgendermaßen aus:
PS C:\> $Eingabe=‘TecChannel‘
PS C:\> $Eingabe -match "\w"
True
PS C:\ $matches
Name Value
0 T
Das mag grundsätzlich ganz praktisch sein, aber viel häufiger wird es wohl sinnvoll sein, genauer auf Übereinstimmung mit dem Suchmuster zu testen. Hier kommen die schon zuvor erwähnten Metazeichen ins Spiel. Sie modifizieren den Vergleich folgendermaßen:
-
*: 0 oder beliebig viele Übereinstimmungen mit dem Zeichen oder der Zeichenklasse
-
+: mindestens eine oder beliebig viele Übereinstimmungen mit dem Zeichen oder der Zeichenklasse
-
?: genau eine Übereinstimmung mit dem Zeichen oder der Zeichenklasse
-
^: Übereinstimmung mit dem Beginn der Zeichenfolge oder Zeichenklasse
-
$: Übereinstimmung mit dem Ende der Zeichenfolge oder Zeichenklasse
Beispiele für Vergleiche und Treffer
Das Vorhergehende mag nun ein wenig verwirrend erscheinen, deshalb ist es am besten, diese "Treffer" einmal selbst anhand der folgenden Beispiele auszuprobieren. So kann es durchaus nicht im Sinne des Anwenders sein, dass der Vergleich sofort nach dem ersten Treffer stoppt. Dieses Verhalten kann folgendermaßen geändert werden:
PS C:\> $Eingabe=‘Tec789Channel‘
PS C:\> $Eingabe -match "\w+"
True
PS C:\ $matches
Name Value
0 Tec789Channel
Geht es darum, auf Zahlen zu vergleichen, so hilft folgender Aufruf:
PS C:\> $Eingabe -match "\d"
True
PS C:\ $matches
Name Value
0 7
Auch hier stoppt der Vergleich nach der ersten Zahl und kann entsprechend erweitert werden:
PS C:\> $Eingabe -match "\d+"
True
PS C:\ $matches
Name Value
0 789
Mithilfe der geschweiften Klammern kann hier auch eine Anzahl von Zeichen festgelegt werden, die in der Zeichenkette zu finden sein soll. Dabei gilt grundsätzlich, dass ein solcher Aufruf in der Form {Anzahl der minimal vorhandenen Zeichen, Anzahl der maximal vorhandenen Zeichen} erfolgt. Befindet sich nur eine Zahl zwischen den geschweiften Klammern, so prüft die PowerShell, ob mindestens die Anzahl dieser Zeichen vorhanden ist. Der folgende Aufruf zeigt dies:
PS C:\> $Eingabe -match "\d{2}"
True
PS C:\ $matches
Name Value
0 78
während dieser Aufruf:
PS C:\> $Eingabe -match "\d{2,3}"
True
PS C:\ $matches
Name Value
0 789
als "wahr" angezeigt wird, wenn mindestens zwei und höchstens drei Ziffern in der Zeichenfolge zu finden sind. Zusammen mit den Zeichen, die einen String am Anfang (^) und am Ende ($) vergleichen, kann hier auch gleich ein praxisnäheres Beispiel gezeigt werden, nämlich die Abfrage eines Eingabe-Strings:
PS C:\> $Eingabe=Read-Host "Bitte drei Ziffern gefolgt von mindestens vier, aber nicht mehr als acht Buchstaben eingeben!"
Wenn nun eine Eingabe in diese Variable erfolgt ist, kann sie dann beispielsweise mit folgendem regulären Ausdruck auf ihre Richtigkeit überprüft werden:
PS C:\ > $Eingabe -match "^\d{3}\w{4,8}$"
Wenn Ihnen das immer noch ein wenig kryptisch erscheint, dann probieren Sie es einfach aus. Diese Art der Abfrage ist wohl nicht für ein sicheres Passwort geeignet, kann aber gut zum Verhindern von falschen Eingaben in Shell-Skripten zum Einsatz kommen.
Es geht noch mehr: Bereiche untersuchen, Groß- und Kleinschreibung …
Während sich die geschweiften Klammern, wie gerade gezeigt, zum Abprüfen einer bestimmten Anzahl von Übereinstimmungen einsetzen lassen, können die eckigen Klammer "[ ]" dazu verwendet werden, einen bestimmten Bereich von Zeichen zu vergleichen. Werden dabei zwischen den Klammer mehrere Zeichen angeben, so ist die Bedingung dann zutreffend, wenn mindestens eines dieser Zeichen im zu untersuchenden Objekt zu finden ist:
PS c:\ > $Eingabe=‘TecChannel Rulez‘
PS c:\> $Eingabe -match "[cz]"
True
PS c:\> $matches
Name Value
0 c
Wie bereits bekannt, stoppt die Shell den Vergleich, sobald eine erste Übereinstimmung gefunden wurde, was in diesem Fall dem ersten Auftauchen des Buchstaben "c" entspricht. Es ist dabei ebenfalls möglich, einen Bereich anzugeben, aus dem beispielsweise die Buchstaben stammen sollen:
PS c:\ > $Eingabe=‘TecChannel Rulez‘
PS c:\> $Eingabe -match "[b-m]"
True
PS c:\> $matches
Name Value
0 e
Hier wird das erste Zeichen gefunden, das aus dem Bereich von "b" bis "m" (einschließlich!) stammt, somit ist das "e" der erste Treffer. In vielen anderen Script-Sprachen wird an dieser Stelle von den regulären Ausdrücken auch eine Case-sensitive Unterscheidung unterstützt. Und so findet man leider auch in vielen Beschreibungen zur PowerShell immer wieder die falsche Behauptung, unter der PowerShell würde diese Vorgehensweise ebenso funktionieren. Testen Sie das einfach, indem Sie mit unserem Teststring (oder natürlich auch einer anderen Zeichenkette) nach dem ersten Vorkommen eines Großbuchstabens suchen. Der folgende Ansatz:
PS c:\ > $Eingabe=‘TecChannel Rulez‘
PS c:\> $Eingabe -match "[A-D]"
True
PS c:\> $matches
Name Value
0 c
sollte dann ja eigentlich erst beim zweiten "c" stoppen, was aber leider weder unter der Version 2.0 noch unter der aktuellen Version 3.0 bei Windows 8 und Windows Server 2012 nicht funktioniert. Hier muss unter der PowerShell eine Variante des -match-Operators zum Einsatz kommen. Wollen Sie reguläre Zeichen Case-sensitiv untersuchen, so müssen Sie dazu -cmatch verwenden, und Sie bekommen das gewünschte Ergebnis:
PS c:\ > $Eingabe=‘TecChannel Rulez‘
PS c:\> $Eingabe -cmatch "[A-D]"
True
PS c:\> $matches
Name Value
0 C
Metazeichen und ihre unterschiedliche Bedeutung
Wie bereits eingangs erwähnt, schrecken viele Anwender und auch System-Profis vor dem Einsatz der regulären Ausdrücke zurück, da sie deren Syntax als zu kryptisch und unübersichtlich ansehen.
Ein Punkt, der sicherlich entscheidend zu dieser weit verbreiteten Einschätzung beigetragen hat, ist die Tatsache, dass einige der Metazeichen durchaus unterschiedliche Bedeutung haben können, je nachdem, wo sie zum Einsatz kommen.
So haben wir in diesem Beitrag bereits das Metazeichen "^" (accent circumflex) vorgestellt, das es erlaubt, die Übereinstimmungen am Anfang einer Zeichenfolge zu finden. Kommt es allerdings innerhalb der eckigen Klammern zum Einsatz, so negiert es das darin formulierte Suchmuster. So findet der folgende Aufruf:
PS c:\ > $Eingabe=‘TecChannel678 Rulez‘
PS c:\> $Eingabe -match "[^a-z]"
True
PS c:\> $matches
Name Value
0 6
die Zeichen, die keine (!) Buchstaben sind - der Vergleich stoppt dementsprechend bei der Ziffer "6". Sollen alle zusammenhängenden Ziffern in dieser Zeichenkette gefunden werden, so würde das durch den folgenden Aufruf erledigt:
PS c:\> $Eingabe -match "[^a-z]+"
True
PS c:\> $matches
Name Value
0 678
Wird ein solcher Aufruf mit der Case-sensitiven Variante des -match-Operators in der folgenden Form gestartet:
PS c:\> $Eingabe -cmatch "[^a-z]"
True
PS c:\> $matches
Name Value
0 T
so stoppt der Vergleich schon beim ersten groß geschriebenen "T", da es nicht in den Bereich "a-z" hineinfällt und diese Bedingung somit zutrifft.
Sonder- und Escape-Zeichen und natürlich die Hochkommas
Neben der Problematik der Metazeichen, die in verschiedenen Zusammenhängen unterschiedliche Effekte hervorrufen, kommt es gerade beim Vergleich und bei der Untersuchung von Zeichenketten immer wieder zu einem anderen Problem: Die Metazeichen, die von den regulären Ausdrücken verwendet werden, tauchen häufig auch als Sonderzeichen in Zeichenketten auf und wollen dann entsprechend behandelt werden.
Grundsätzlich gilt dabei: Sollen diese Meta- oder Sonderzeichen nicht mit ihrer Funktion aufgerufen werden, so müssen sie maskiert werden. Das gelingt mit dem sogenannten "Escape"-Zeichen, das den regulären Zeichen dann mitteilt, dass sie das nächste Zeichen hinter diesem Escape-Zeichen genau so "betrachten" sollen, wie es da steht:
PS c:\> ‘Der $hell Fisch‘ -match "\$\w+"
True
Name Value
0 $hell
durch die Shell - so konnten wir hier erreichen, dass das Dollarzeichen auch als solches gesehen wird und die Shell nicht versucht, hier den Inhalt der Variablen "$hell" einzusetzen. Sie können den Unterschied leicht erkennen, wenn Sie diese Form der Eingabe ausprobieren:
PS c:\> "Der $hell Fisch" -match "\$\w+"False
Haben Sie zuvor einen anderen Aufruf mit dem -match-Operator ausgeführt, so kann es Ihnen allerdings jetzt passieren, dass Sie den vorherigen Wert in $matches finden. Besetzen Sie also sicherheitshalber diese Variable mit einem Leerstring in der folgenden Form, bevor Sie dieses Experiment ausführen:
$matches=""
Die Möglichkeiten, die Ihnen mit der PowerShell und unter Einsatz von regulären Zeichen zur Verfügung stehen, sind enorm - also haben Sie keine Scheu und probieren Sie diese mithilfe unseres Workshops einfach aus. Es gibt noch viel mehr Einzelheiten, Sonderzeichen und Tricks zu zeigen, aber das würde den Rahmen dieses Artikels deutlich sprengen.
Zum Abschluss haben wir hier noch vier praxisnahe Beispiele herausgesucht. Überlegen Sie zunächst, was diese Aufrufe wohl anzeigen werden, und probieren Sie diese aus. In unserer Bilderstrecke können Sie dann auch Aufruf und Ausführung dieser Aufrufe sehen, wie sie auf unseren Testsystemen unter Windows 7 und Windows 8 abgelaufen sind:
dir c:\windows | where {$_.name -match "\s"}
(\s vergleicht auf "Whitespaces - also Leerzeichen und so weiter)
get-process [bw]*
get-service | where {$_.name -match "\d{1}"}
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\kb[0-9]*
In unserer Bilderstrecke können Sie dann auch Aufruf und Ausführung dieser Aufrufe sehen, wie sie auf unseren Testsystemen unter Windows 7 und Windows 8 abgelaufen sind. (mje)