Schleifen in PowerShell: For, Foreach, While, Do-Until, Continue, Break


    Tags:

    Powershell-LogoWie die meisten proze­duralen Programmier­sprachen verfügt PowerShell über eine ganze Palette von Schleifen­konstruk­tionen sowie ergän­zende Anwei­sungen zur Steuerung von Itera­tionen. Eine Besonder­heit besteht darin, dass neben foreach noch Foreach-Object existiert, um über Elemente eines Arrays zu iterieren.

    Wer vor PowerShell bereits mit anderen Sprachen gearbeitet hat, wird sich schnell mit den verschiedenen Schleifentypen zurechtfinden. Microsoft orientierte sich bei der Funktion und Syntax an Schleifen in C und davon abgeleiteten Sprachen wie Javascript.

    So steht die Schleifenbedingung üblicherweise in runden Klammern, während der Schleifenkörper von geschweiften Klammern umgeben wird. Gewöhnungsbedürftig sind indes die Vergleichsoperatoren, die man zur Formulierung der Schleifenbedingungen benötigt. Außerdem sind die Befehlswörter in PowerShell nicht case-sensitive, so dass man for, For oder FOR schreiben kann.

    Schleifen für verschiedene Zwecke

    Die Schleifen-Varianten decken wie gewohnt jeweils spezifische Anforderungen ab. Dazu zählt etwa die Unterscheidung zwischen abweisenden und nicht-abweisenden Schleifen, so dass die Bedingung wahlweise am Anfang oder am Ende des Schleifenkörpers geprüft wird.

    Benötigt man eine Laufvariable, die man gleich in der Schleifenanweisung initialisieren und inkrementieren kann, dann empfiehlt sich eine for-Schleife, reicht dagegen die Prüfung einer Bedingung, dann greift man zu while.

    for mit integrierter Laufvariable

    Die for-Schleife in PowerShell birgt keine großen Überraschungen und verhält sich wie von anderen Programmiersprachen gewohnt:

    for ($i=1; $i -le 10; $i++) {$i,"`n"}

    Dieses simple Beispiel initialisiert die Laufvariable $i mit dem Wert 1 und zählt sie bei jedem Durchlauf mit Hilfe des Inkrementoperators um 1 hoch, bis sie 10 erreicht und damit die Abbruchbedingung erfüllt ist. Bei jeder Iteration wird der Wert von $i plus ein Zeilenvorschub ausgegeben.

    while-Schleife

    Auch die while-Schleife entspricht dem, was man von einigen anderen Sprachen kennt. Es handelt sich dabei um eine abweisende Schleife, weil die Anweisungen des Schleifenkörpers kein einziges Mal ausgeführt wird, wenn die Schleifenbedingung vor dem ersten Durchlauf nicht erfüllt ist.

    Im Gegensatz zur for-Schleife besteht der Ausdruck nur aus einer Bedingung. Will man eine Laufvariable verwenden, dann muss man sie vor dem while-Statement initialisieren und im Schleifenkörper hoch- oder herunterzählen.

    Folgendes Fragment zeigt, wie man while für ein einfaches Menüsystem verwenden könnte. Bei jedem Durchlauf wird der Benutzer zur Eingabe eines Zeichens aufgefordert, mit dem er einen Menüpunkt auswählt. Die Eingabe von "Q" beendet die Schleife:

    while(($inp = Read-Host -Prompt "Wählen Sie einen Befehl") -ne "Q"){
    switch($inp){
    L {"Datei wird gelöscht"}
    A {"Datei wird angezeigt"}
    R {"Datei erhält Schreibschutz"}
    Q {"Ende"}
    default {"Ungültige Eingabe"}
    }
    }

    Die while-Schleife prüft die Bedingung vor dem ersten Durchlauf, wobei man dort auch Cmdlets aufrufen und Werte zuweisen darf.

    Nicht-abweisende Schleifen: do-while, do-until

    PowerShell kennt darüber hinaus zwei Formen von nicht-abweisenden Schleifen, nämlich do-while und do-until. In beiden Fällen werden die Anweisungen des Schleifenkörpers mindestens einmal ausgeführt, weil die Bedingung erst am Ende der Schleife steht.

    Syntaktisch sind do-while und do-until identisch, der Unterschied ist logischer Natur: do-while läuft, solange die Bedingung wahr ist und bricht ab, wenn sie nicht mehr erfüllt wird. Bei do-until ist es genau umgekehrt: Sie endet, wenn die Schleifenbedingung den Wert TRUE annimmt. Die allgemeine Form dieser beiden Schleifen sieht so aus:

    do{
    Anweisungen des Schleifenkörpers
    }
    while/until(Bedingung)

    Grundsätzlich können sich do-until und do-while gegenseitig ersetzen, indem man die Bedingung negiert.

    Schleifensteuerung mit break und continue

    PowerShell erlaubt eine zusätzliche Schleifensteuerung durch die Anweisungen break und continue. Auch sie sind von anderen Sprachen bekannt und werden in gut strukturierten Scripts so gut wie nie gebraucht. Vor dem Einsatz dieser Sprachmittel sollte man überlegen, ob sie sich nicht durch einen anderen Algorithmus vermeiden lassen. Sie machen Code schwerer verständlich und können zu unerwarteten Nebeneffekten führen.

    Die break-Anweisung lässt sich nicht nur in allen Schleifentypen nutzen, sondern auch innerhalb von SWITCH-Blöcken. Sie bewirkt dabei grundsätzlich immer das Gleiche, nämlich das Verlassen des Blocks und wenn vorhanden, die Übergabe der Kontrolle an eine übergeordnete Anweisung. Dieses Kommando lässt sich mit exit for oder exit do in VBScript vergleichen.

    Der Einsatz von break in Schleifen führt zum Abbruch der Iteration und der Fortsetzung des Programmablaufs mit der nächsten Anweisung außerhalb des Schleifenkörpers. Bei verschachtelten Schleifen wäre dies der übergeordnete for-, while-, foreach- oder do-Block. Die wohl einzig sinnvolle Verwendung von break macht seine Ausführung von einer if-Bedingung abhängig, weil die Schleife sonst keinen zweiten Durchlauf erleben wird.

    break mit Sprungziel

    Eine Besonderheit des break-Statements in PowerShell besteht darin, dass man es mit der Angabe einer Sprungmarke kombinieren kann. Es funktioniert dann ähnlich wie das berüchtigte GoTo in Basic, bloß mit der Einschränkung, dass Sprungziele nur in einer Zeile mit einer for-, while-, foreach- oder do-Anweisung stehen können. Ihrem Namen muss ein Doppelpunkt vorangestellt werden:

    while(Bedingung){
    Anweisung des Schleifenkörpers
    if(Bedingung) {break :DoSchleife}
    Anweisung des Schleifenkörpers
    }

    :DoSchleife do{
    Anweisung des Schleifenkörpers
    }
    until(Bedingung)

    In diesem Schema würde die Programmausführung zum Beginn der do-Schleife wechseln, wenn die if-Bedingung innerhalb des while-Blocks zutrifft.

    continue-Anweisung

    Die continue-Anweisung bricht ebenfalls die weitere Ausführung des Schleifenkörpers ab. Sie bewirkt aber nicht die Fortsetzung des Programmflusses außerhalb der Schleife, sondern setzt die Abarbeitung der Schleife mit der nächsten Iteration fort. Auch hier gilt, dass man mit continue möglichst sparsam umgehen sollte.

    Auch wenn PowerShell das ganze Arsenal an Schleifen bietet, die man besonders von kompilierenden Sprachen kennt, wird man diese sprachlichen Mittel selten ausschöpfen, weil die meisten Scripts eine relativ einfache Programmlogik aufweisen. Der mit Abstand häufigste Einsatz von Schleifen, besonders im interaktiven Betrieb von PowerShell, dient der Iteration über die Elemente von Arrays, nachdem zahlreiche Cmdlets solche Datentypen zurückgeben.

    Implizite Iteration über Array-Elemente

    Aus diesem Grund vereinfachte Microsoft derartige Operationen und lässt die Benutzer dafür zwischen verschiedenen Methoden wählen. Die einfachste und eleganteste davon ist das implizite foreach, das seit PowerShell 3.0 die Verwendung dieses Schlüsselwortes in einigen Fällen vermeidet. So kann man eine bestimmte Eigenschaft von allen Elementen eines Arrays ausgeben, ohne dafür eine Schleife und Select-Object einzusetzen:

    (Get-ADUser -Filter *).Surname

    Dieser Aufruf von Get-ADUser ermittelt alle User-Objekte im Active Directory und gibt diese als Array zurück. Der obige Ausdruck zeigt für jedes Element den Nachnamen an.

    Foreach und Foreach-Object

    Möchte man mehr tun als nur eine Eigenschaft der Array-Elemente auszugeben, beispielsweise deren Werte abfragen und weiterverarbeiten, dann braucht man eine explizite Schleife. Für diesen Fall kennt PowerShell einerseits die foreach-Anweisung und andererseits das Cmdlet Foreach-Object. Verwirrung kann hier die Tatsache stiften, dass für Letzteres das vordefinierte Alias foreach (zusätzlich zu "%") existiert.

    Das foreach-Kommando entspricht in seiner Logik seinem Pendant in VBScript, die Unterschiede sind nur syntaktischer Natur. So heißt das Schlüsselwort foreach statt for each und der Schleifenkörper wird nicht durch eine next-Anweisung, sondern durch eine geschweifte Klammer abgeschlossen:

    $user = Get-ADUser -Filter *
    foreach($u in $user) {
    $u.surname
    }

    Diese Zeilen erledigen die gleiche Aufgabe wie das Beispiel für die implizite Schleife. Das Ergebnis von Get-ADUser wird jedoch erst der Variablen $user zugewiesen. Anschließend benötigt man in der foreach-Schleife eine weitere Variable ($u), die stets das aktuelle Element des Arrays hält.

    Will man das obige Problem mit Hilfe von Foreach-Object lösen, dann schickt man den Output von Get-ADUser durch eine Pipe an Foreach-Object und verwendet dort die automatische Variable $_, um das aktuelle Element zu referenzieren:

    Get-ADUser -Filter *| %{ if($_.surname) {(Get-Culture).TextInfo.ToTitleCase( $_.surname)}}

    Dieses Beispiel liest nicht nur den Nachnamen der AD-User aus, sondern konvertiert seine Initialen mit der Funktion ToTitleCase zu Großschreibung, wenn die Eigenschaft surname ungleich NULL ist.

    Da Foreach-Object kürzere Befehle und weniger Tipparbeit bedeutet (nicht nur wegen des Alias "%"), scheint es keinen Grund zu geben, die foreach-Anweisung zu verwenden. Bei einer großen Zahl an Schleifendurchläufen mit relativ komplexen Berechnungen im Schleifenkörper ist die herkömmliche foreach-Schleife jedoch erheblich schneller.

    Täglich Know-how für IT-Pros mit unserem Newsletter

    Wir ver­wenden Ihre Mail-Adresse nur für den Ver­sand der News­letter.
    Es erfolgt keine per­sonen­be­zogene Auswertung.

    Bild von Wolfgang Sommergut
    Wolfgang Sommergut hat lang­jährige Erfahrung als Fach­autor, Berater und Kon­ferenz­sprecher zu ver­schie­denen Themen der IT. Da­ne­ben war er als System­ad­mi­ni­stra­tor und Con­sultant tätig.
    // Kontakt: E-Mail, XING, LinkedIn //

    Verwandte Beiträge

    Weitere Links

    2 Kommentare

    Hallo Herr Sommergut!

    Wieder einmal ein schöner Artikel über Windows PowerShell und seine Schleifen.

    Da PowerShell Neulinge immer wieder über die 2 verschiedenen ForEach Konstrukte stolpern, habe ich auch einen ausführliche Artikel dazu geschrieben.

    Hier werden die beiden Windows PowerShell ForEach Konstrukte verglichen:

    PowerShell ForEach und ForEach-Object
    http://www.admin-source.de/BlogDeu/905/powershell-foreach-und-foreach-ob...

    Gruss
    Peter Kriegel
    deutschsprachige Windows PowerShell Community
    http://www.PowerShell-Group.eu

    for mit integrierter Laufvariable

    Die for-Schleife in PowerShell ... :

    for ($i=1; $i -le 10; $i++) {$i,"`n"}

    Dieses simple Beispiel initialisiert die Laufvariable $i mit dem Wert 1 und zählt sie bei jedem Durchlauf mit Hilfe des Inkrementoperators um 1 hoch, bis sie 10 erreicht und damit die Abbruchbedingung erfüllt ist...

    Na ja, eigentlich ist es keine Abbruchbedingung, sondern eine Fortsetzungsbedingung.
    Das mag den meisten klar sein, aber gerade Anfänger könnten auch darüber stolpern.