Windows PowerShell Schreiben regulärer Ausdrücke

Don Jones

192.168.4.5. \\Server57\Share. johnd@contoso.com. Ohne Zweifel erkennen Sie, dass diese drei Elemente eine IP-Adresse, einen UNC-Pfad (Universal Naming Convention) und eine E-Mail-Adresse repräsentieren. Ihr Gehirn erkennt die entsprechenden Formate. Vier Gruppen von Ziffern, umgekehrte Schrägstriche, das Symbol @ und weitere Hinweise deuten darauf hin, für welche Datentypen diese Zeichenfolgen

stehen. Ohne großes Nachdenken ist Ihnen schnell klar, dass 192.168 für sich genommen keine gültige IP-Adresse darstellt, dass 7\\Server2\\Share kein gültiger UNC-Pfad ist und joe@contoso keine gültige E-Mail-Adresse repräsentiert.

Computer müssen leider ein bisschen härter arbeiten, um komplizierte Formate wie diese zu „verstehen“. An diesem Punkt kommen reguläre Ausdrücke ins Spiel. Ein regulärer Ausdruck ist eine in einer besonderen Sprache für reguläre Ausdrücke geschriebene Zeichenfolge, die einem Computer dabei hilft, Zeichenfolgen eines bestimmten Formats zu identifizieren, z. B. eine IP-Adresse, einen UNC-Pfad oder eine E-Mail-Adresse. Ein gut geschriebener regulärer Ausdruck ermöglicht es einem Windows PowerShellTM-Skript, Daten als gültig zu akzeptieren, oder Daten, die dem von Ihnen angegebenen Format nicht entsprechen, als ungültig zurückzuweisen.

Herstellen einer einfachen Übereinstimmung

Der Operator „–match“ der Windows PowerShell vergleicht eine Zeichenfolge mit einem regulären Ausdruck, auch regex genannt, und gibt dann „True“ oder „False“ zurück. Welcher Wert zurückgegeben wird, hängt davon ab, ob die Zeichenfolge dem regex entspricht. Ein sehr einfacher regex muss nicht einmal eine besondere Syntax enthalten, Literalzeichen sind ausreichend. Beispiel:

"Microsoft" –match "soft"
"Software" –match "soft"
"Computers" –match "soft"

Bei der Ausführung in Windows PowerShell geben die ersten beiden Ausdrücke „True“ zurück, während der dritte „False“ zurückgibt. In jedem folgt auf eine Zeichenfolge der Operator „–match“, dem wiederum ein regex folgt. Standardmäßig bewegt sich ein regex über einer Zeichenfolge, um eine Entsprechung zu finden. Die Zeichen „soft“ sind sowohl in Software als auch Microsoft enthalten, jedoch an verschiedenen Positionen. Bei einem regex wird standardmäßig die Groß-/Kleinschreibung nicht beachtet: „soft“ wird trotz des S in Großschreibung in „Software“ gefunden.

Falls erforderlich kann ein anderer Operator, nämlich „–cmatch“, für einen regex-Vergleich unter Beachtung der Groß-/Kleinschreibung herangezogen werden:

"Software" –cmatch "soft"

Dieser Ausdruck gibt „False“ zurück, da die Zeichenfolge „soft“ in einem Vergleich unter Beachtung der Groß-/Kleinschreibung nicht mit „Software“ übereinstimmt. Außerdem ist der Operator „–imatch“ verfügbar, mit dem die Beachtung der Groß-/Kleinschreibung explizit ausgeschlossen werden kann, obgleich dies dem Standardverhalten von „–match“ entspricht.

Platzhalter und Repeater

Ein regex kann einige Platzhalterzeichen enthalten. Ein Punkt entspricht beispielsweise einer Instanz eines beliebigen Zeichens. Ein Fragezeichen entspricht null oder einer Instanz eines beliebigen Zeichens. Hier einige Beispiele zur Illustration:

"Don" –match "D.n" (True)
"Dn" –match "D.n" (False)
"Don" –match "D?n" (True)
"Dn" –match "D?n" (True)

Im ersten Ausdruck steht der Punkt für genau ein Zeichen, deshalb wird „True“ zurückgegeben. Im zweiten Ausdruck findet der Punkt das eine Zeichen, das enthalten sein muss, nicht, also wird „False“ zurückgegeben. Das Fragezeichen, wie im dritten und vierten Ausdruck gezeigt, kann einem einzigen unbekannten Zeichen oder gar keinem Zeichen entsprechen. Im vierten Beispiel schließlich wird „True“ zurückgegeben, weil sowohl „D“ als auch „n“ ohne ein Zeichen dazwischen gefunden werden. Folglich kann das Fragezeichen als Zeichen betrachtet werden, das für ein optionales Zeichen steht. Die Rückgabe ist also nach wie vor „True“, selbst wenn an dieser Position kein Zeichen vorhanden ist.

Ein regex erkennt außerdem die Symbole * und + als Repeater. Diese müssen auf ein oder mehrere Zeichen folgen. Das Symbol * entspricht null oder mehr der angegebenen Zeichen, während das Symbol + einem oder mehreren der angegebenen Zeichen entspricht. Hier einige Beispiele:

"DoDon" –match "Do*n" (True)
"Dn" -match "Do*n" (True)
"DoDon" -match "Do+n" (True)
"Dn" -match "Do+n" (False)

Beachten Sie, dass sowohl * und + nicht nur dem „o“, sondern auch „Do“ entsprechen. Repeater sind nämlich so konstruiert, dass sie nicht nur einem Zeichen, sondern einer Reihe von Zeichen entsprechen.

Was geschieht nun, wenn der Punkt oder die Symbole *, ? oder + sich selbst entsprechen müssen? Sie stellen Ihnen einfach einen umgekehrten Schrägstrich voran. Der umgekehrte Schrägstrich ist das regex-Escapezeichen:

"D.n" -match "D\.n" (True)

Beachten Sie, dass sich dieses Zeichen vom Windows PowerShell-Escapezeichen (dem umgekehrten Apostroph) unterscheidet, jedoch dem Industriestandard für die regex-Syntax folgt.

Zeichenklassen

Eine Zeichenklasse ist eine umfassendere Form des Platzhalters, die für eine ganze Gruppe von Zeichen steht. Windows PowerShell ist in der Lage, relativ viele Zeichenklassen zu erkennen. Beispiel:

  • \w entspricht einem beliebigen Wortzeichen, das heißt Buchstaben und Zahlen.
  • \s entspricht einer beliebigen Leerstelle, z. B. Tabstopps, Leerzeichen usw.
  • \d entspricht einer beliebigen Ziffer.

Darüber hinaus gibt es negative Zeichenklassen: \W steht für ein beliebiges Nichtwortzeichen, \S für Nichtleerstellenzeichen und \D für alle Zeichen, die keine Ziffern sind. Diesen Klassen kann das Symbol * oder + folgen, zum Zeichen dafür, dass mehrere Übereinstimmungen akzeptabel sind. Hier einige Beispiele:

"Shell" -match "\w" (True)
"Shell" -match "\w*" (True)

Cmdlet des Monats

Das Write-Debug-Cmdlet eignet sich sehr gut für das Schreiben von Objekten (z. B. Textzeichenfolgen) in die Debug-Pipeline. Der Versuch, dieses Cmdlet in der Shell einzusetzen, kann allerdings ein wenig enttäuschend ausfallen, da der Eindruck entsteht, dass das Cmdlet nichts bewirkt.

Das liegt daran, dass die Debug-Pipeline standardmäßig deaktiviert ist: Für die $DebugPreference-Variable wurde „SilentlyContinue“ festgelegt. Wenn Sie den Wert in „Continue“ ändern, wird alles, was Sie mit dem Cmdlet „Write-Debug“ senden, auf der Konsole in Gelb angezeigt. Damit bietet sich eine perfekte Möglichkeit, Ihren Skripts Ablaufverfolgungscode hinzuzufügen, mit dem Sie die Ausführung eines komplexen Skripts verfolgen können. Die gelbe Farbe erleichtert es, zwischen der Verfolgung und der normalen Ausgabe des Skripts zu unterscheiden, und Sie können die Debugnachrichten jederzeit deaktivieren, ohne alle Write-Debug-Anweisungen entfernen zu müssen. Legen Sie einfach für die $DebugPreference-Variable wieder „SilentlyContinue“ fest. Der Debugtext wird dann unterdrückt.

Obwohl beide Ausdrücke „True“ zurückgeben, werden völlig verschiedene Entsprechungen gefunden. Glücklicherweise gibt es eine Möglichkeit zu überprüfen, wie der Operator „–match“ genau arbeitet: Bei jeder Übereinstimmung wird eine besondere Variable namens „$matches“ mit den Ergebnissen der Übereinstimmung aufgefüllt (also mit den Zeichen in der Zeichenfolge, die der Operator im Vergleich mit dem regex gefunden hat). Die $matches-Variable behält ihre Ergebnisse bei, bis eine weitere positive Übereinstimmung mithilfe des Operators „–match“ gefunden wurde. Abbildung 1 zeigt den Unterschied zwischen den beiden Ausdrücken, die soeben vorgestellt wurden. Wie Sie sehen können, entsprach \w dem „S“ in „Shell“, während das sich wiederholende \w* dem gesamten Wort entsprach.

Abbildung 1 Die Macht eines *

Abbildung 1 Die Macht eines *(Klicken Sie zum Vergrößern auf das Bild)

Zeichengruppen, Zeichenbereiche und Größen

Ein regex kann auch Zeichengruppen oder Zeichenbereiche enthalten, die in eckige Klammern eingeschlossen sind. So bedeutet beispielsweise [aeiou], dass jedes der eingeschlossenen Zeichen – a, e, i, o oder u – als Übereinstimmung akzeptiert wird. [a-zA-Z] gibt an, dass jeder Buchstabe im Bereich von A bis Z akzeptiert wird (wenn Sie den Operator „–match“ verwenden, bei dem Groß-/Kleinschreibung keine Rolle spielt, wäre jedoch a-z oder A-Z ausreichend). Hier ist ein Beispiel:

"Jeff" -match "J[aeiou]ff" (True)
"Jeeeeeeeeeeff" -match "J[aeiou]ff" (False)

Mithilfe geschweifter Klammern können Sie auch ein Minimum und ein Maximum für die Anzahl der Zeichen angeben. {3} gibt an, dass genau drei der angegebenen Zeichen erforderlich sind, {3,} bedeutet, dass die Zahl der Zeichen mindestens drei betragen muss, und {3,4} gibt an, dass mindestens drei, jedoch höchstens vier Zeichen erwünscht sind. Dies ist eine ideale Möglichkeit zum Erstellen eines regex für IP-Adressen:

"192.168.15.20" -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" (True)

Für diesen regex sind vier Zifferngruppen mit jeweils einer bis drei Ziffern erforderlich, die alle durch einen Literalpunkt voneinander getrennt sind. Sehen Sie sich dieses Beispiel an:

"300.168.15.20" -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" (True)

Dies veranschaulicht die Einschränkungen eines regex. Die Formatierung dieser Zeichenfolge sieht zwar wie eine IP-Adresse aus, ist jedoch offensichtlich keine gültige IP-Adresse. Ein regex kann nicht feststellen, ob Daten tatsächlich gültig sind, sondern lediglich, ob die Daten das richtige Aussehen haben.

Schränken Sie die Bewegung ein

Die Problembehandlung bei einem regex kann schwierig sein. Es folgt ein regex zur Prüfung auf einen UNC-Pfad im Format \\Server2\Share:

"\\Server2\Share" -match "\\\\\w+\\\w+" (True)

Der regex selbst ist in diesem Fall schwer zu lesen, da jeder literale umgekehrte Schrägstrich, der überprüft werden soll, mit einem zweiten umgekehrten Schrägstrich als Escapezeichen versehen werden muss. Dies scheint zwar gut zu funktionieren, der Schein trügt jedoch:

"57\\Server2\Share" -match "\\\\\w+\\\w+" (True)

Dieses zweite Beispiel stellt eindeutig keinen gültigen UNC-Pfad dar, wurde jedoch vom regex als gültig anerkannt. Warum? Wie bereits erwähnt, ist ein regex standardmäßig nicht verankert. Dieser regex sucht lediglich nach zwei umgekehrten Schrägstrichen, einem oder mehreren Buchstaben und einer oder mehreren Zahlen, einem weiteren umgekehrten Schrägstrich und weiteren Buchstaben und Zahlen. Dieses Muster ist in der Zeichenfolge vorhanden, jedoch zusammen mit den zusätzlichen Ziffern am Beginn, die dafür sorgen, dass der UNC-Pfad ungültig ist. Der Trick besteht darin, den regex anzuweisen, am Anfang der Zeichenfolge nach Übereinstimmungen zu suchen, ohne sich zu bewegen. Dies kann auf folgende Weise erreicht werden:

"57\\Server2\Share" -match "^\\\\\w+\\\w+" (False)

Das Zeichen ^ zeigt an, dass die Zeichenfolge an dieser Position beginnt. Mit dieser Ergänzung wird der UNC-Pfad als ungültig erkannt, da der regex erfordert, dass die ersten beiden Zeichen umgekehrte Schrägstriche sind – dies ist nicht der Fall.

In ähnlicher Weise kann das Symbol $ dazu verwendet werden, das Ende einer Zeichenfolge anzuzeigen. Im Falle eines UNC-Pfads wäre dies nicht besonders nützlich, da ein UNC-Pfad weitere Pfadsegmente enthalten kann (z. B. \\Server2\Share\Folder\File). Es sind jedoch viele Fälle vorstellbar, in denen es von Vorteil wäre, das Ende einer Zeichenfolge anzugeben.

Hilfe zu regulären Ausdrücken

In Windows PowerShell bietet das Hilfethema „about_regular_expressions“ Unterstützung bei der grundlegenden Syntax für die regex-Sprache. In Onlineressourcen finden Sie jedoch noch weitere Informationen. Eine sehr zu empfehlende Website, www.RegExLib.com, bietet beispielsweise eine kostenlose Bibliothek regulärer Ausdrücke, die für verschiedene Zwecke geschrieben und von der Allgemeinheit zur Verfügung gestellt wurden. Sie können nach Schlüsselwörtern wie „e-mail“ oder „UNC“ suchen, um schnell einen regex zu finden, der Ihren Anforderungen genügt oder zumindest einen guten Ausgangspunkt bietet. Wenn es Ihnen gelingt, einen guten regex zu erstellen, können Sie diesen in der Bibliothek verfügbar machen, damit auch andere ihn verwenden können.

Ebenfalls zu empfehlen ist RegexBuddy (www.RegexBuddy.com). Dies ist ein preiswertes Tool, das einen grafischen regex-Editor bereitstellt. RegexBuddy vereinfacht das Zusammenstellen eines komplexen regex. Außerdem erleichtert dieses Tool das Testen eines regex, um sicherzustellen, dass gültige Zeichenfolgen ordnungsgemäß akzeptiert und ungültige zurückgewiesen werden. Etliche andere Softwareentwickler haben kostenlose bzw. kommerziell vertriebene regex-Editoren und Tester sowie Sharewareprodukte herausgebracht, die für Benutzer sicher nützlich sind.

Verwendung regulärer Ausdrücke

Sie mögen sich fragen, in welcher konkreten Situation Sie einen regex verwenden können. Angenommen Sie lesen die Informationen einer CSV-Datei und verwenden diese Informationen zum Erstellen neuer Benutzer in Active Directory®. Wenn die CSV-Datei von einer anderen Person generiert wird, möchten Sie sicher überprüfen, ob die Daten das richtige Aussehen haben. Ein regex eignet sich hervorragend für diese Aufgabe. Ein einfacher regex wie \w+ kann bestätigen, dass die Vor- und Nachnamen keine Sonderzeichen oder Symbole enthalten. Mit einem etwas komplexeren regex lässt sich bestätigen, dass die E-Mail-Adressen Ihrem Unternehmensstandard entsprechen. Sie könnten folgendes Beispiel verwenden:

"^[a-z]+\.[a-z]+@contoso.com$"

Dieser regex erfordert eine E-Mail-Adresse im Format don.jones@contoso.com, wobei der Vor- und der Nachname nur Buchstaben enthalten dürfen und durch einen Punkt voneinander getrennt sein müssen. Für E-Mail-Adressen einen regex zu schreiben, ist übrigens eine schwierige Aufgabe. Wenn Sie den Bereich auf einen bestimmten Unternehmensstandard einengen, erleichtern Sie sich die Aufgabe.

Denken Sie daran, die Verankerungen am Beginn und Ende zu verwenden (^ und $), die sicherstellen, dass auf contoso.com nichts folgt und nichts den Zeichen vorausgeht, aus denen der Vorname des Benutzers besteht.

Die Verwendung dieses regex in Windows PowerShell ist eine relativ leichte Aufgabe. Angenommen die Variable „$email“ enthält die E-Mail-Adresse, die Sie aus der CSV-Datei lesen. Dann können Sie deren Gültigkeit mit einer Funktion ähnlich der folgenden überprüfen:

$regex = "^[a-z]+\.[a-z]+@contoso.com$"
If ($email –notmatch $regex) {
  Write-Error "Invalid e-mail address $email" 
}

In diesem Beispiel haben Sie außerdem einen neuen Operator kennengelernt. „-notmatch“ gibt „True“ zurück, wenn die Zeichenfolge dem angegebenen regex nicht entspricht. (Für Vergleiche, in denen die Groß-/Kleinschreibung beachtet werden soll, gibt es den Operator „–cnotmatch“.)

Über reguläre Ausdrücke gäbe es noch viel zu sagen, das an dieser Stelle nicht behandelt werden kann: zusätzliche Zeichenklassen, komplexere Operationen und weitere Operatoren. Außerdem gibt es den Objekttyp [regex], der von Windows PowerShell unterstützt wird. Dieser kurze Überblick über die regex-Syntax sollte jedoch als Einstieg genügen. Wenn Sie Hilfe bei einem ganz besonders kniffligen regex benötigen, stehe ich Ihnen unter www.ScriptingAnswers.com jederzeit zur Verfügung.

Don Jones schreibt redaktionelle Beiträge für das TechNet Magazin und ist Mitautor von Windows PowerShell: TFM (SAPIEN Press). Er hält Schulungen zu Windows PowerShell ab (www.ScriptingTraining.com). Sie erreichen ihn über die Website ScriptingAnswers.com.

© 2008 Microsoft Corporation und CMP Media, LLC. Alle Rechte vorbehalten. Die nicht genehmigte teilweise oder vollständige Vervielfältigung ist nicht zulässig.