Kapitel 6: Flusssteuerung

Skripterstellung

Wenn Sie nicht mehr nur noch PowerShell-Einzeiler, sondern ganze Skripts schreiben sollen, klingt dies erst einmal wesentlich komplizierter, als es tatsächlich ist. Ein Skript besteht aus denselben oder ähnlichen Befehlen, die Sie sonst interaktiv in der PowerShell-Konsole ausführen würden, außer dass diese jetzt als .PS1-Datei gespeichert werden. Es gibt einige Skriptkonstrukte, die Sie verwenden können, z. B. eine foreach-Schleife anstelle des Cmdlets ForEach-Object. Für Einsteiger können die Unterschiede verwirrend sein, besonders wenn Sie bedenken, dass foreach sowohl ein Skriptkonstrukt als auch ein Alias für das Cmdlet ForEach-Object ist.

Schleifen

Eine der großartigen Eigenschaften von PowerShell ist, dass es, sobald Sie herausgefunden haben, wie Sie etwas mit einem Element machen, fast genauso einfach ist, dieselbe Aufgabe für Hunderte von Elementen auszuführen. Durchlaufen Sie die Elemente einfach in einer Schleife, indem Sie eine der zahlreichen verschiedenen Arten von Schleifen in PowerShell verwenden.

ForEach-Object

ForEach-Object ist ein Cmdlet zum Durchlaufen von Elementen in einer Pipeline, wie mit PowerShell-Einzeilern. ForEach-Object streamt die Objekte durch die Pipeline.

Obwohl der Parameter Module von Get-Command mehrere Werte akzeptiert, die Zeichenfolgen sind, akzeptiert er sie nur über die Pipelineeingabe mittels Eigenschaftsname oder über Parametereingaben. Wenn Sie im folgenden Szenario zwei Zeichenfolgen als Wert an Get-Command übergeben möchten, um sie mit dem Parameter Module zu verwenden, müssten Sie das Cmdlet ForEach-Object verwenden.

'ActiveDirectory', 'SQLServer' |
   ForEach-Object {Get-Command -Module $_} |
     Group-Object -Property ModuleName -NoElement |
         Sort-Object -Property Count -Descending
Count Name
----- ----
  147 ActiveDirectory
   82 SqlServer

Im vorherigen Beispiel ist das aktuelle Objekt $_. Ab PowerShell-Version 3.0 kann anstelle von $_$PSItem verwendet werden. Ich bin aber der Meinung, dass die meisten erfahrenen PowerShell-Benutzer immer noch lieber $_ verwenden, weil es abwärtskompatibel und weniger einzugeben ist.

Wenn Sie das Schlüsselwort foreach verwenden, müssen Sie zuerst alle Elemente im Arbeitsspeicher speichern, bevor Sie diese durchlaufen, was sich schwierig gestalten kann, wenn Sie nicht wissen, mit wie vielen Elementen Sie arbeiten.

$ComputerName = 'DC01', 'WEB01'
foreach ($Computer in $ComputerName) {
  Get-ADComputer -Identity $Computer
}
DistinguishedName : CN=DC01,OU=Domain Controllers,DC=mikefrobbins,DC=com
DNSHostName       : dc01.mikefrobbins.com
Enabled           : True
Name              : DC01
ObjectClass       : computer
ObjectGUID        : c38da20c-a484-469d-ba4c-bab3fb71ae8e
SamAccountName    : DC01$
SID               : S-1-5-21-2989741381-570885089-3319121794-1001
UserPrincipalName :

DistinguishedName : CN=WEB01,CN=Computers,DC=mikefrobbins,DC=com
DNSHostName       : web01.mikefrobbins.com
Enabled           : True
Name              : WEB01
ObjectClass       : computer
ObjectGUID        : 33aa530e-1e31-40d8-8c78-76a18b673c33
SamAccountName    : WEB01$
SID               : S-1-5-21-2989741381-570885089-3319121794-1107
UserPrincipalName :

Häufig ist eine Schleife wie foreach oder ForEach-Object erforderlich. Andernfalls erhalten Sie eine Fehlermeldung.

Get-ADComputer -Identity 'DC01', 'WEB01'
Get-ADComputer : Cannot convert 'System.Object[]' to the type
'Microsoft.ActiveDirectory.Management.ADComputer' required by parameter 'Identity'.
Specified method is not supported.
At line:1 char:26
+ Get-ADComputer -Identity 'DC01', 'WEB01'
+                          ~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-ADComputer], ParameterBindingExc
   eption
    + FullyQualifiedErrorId : CannotConvertArgument,Microsoft.ActiveDirectory.Management
   .Commands.GetADComputer

In anderen Fällen können sie dieselben Ergebnisse erzielen und gleichzeitig die Schleife vollständig beseitigen. Sehen Sie in der Cmdlet-Hilfe nach, um Ihre Optionen herauszufinden.

'DC01', 'WEB01' | Get-ADComputer
DistinguishedName : CN=DC01,OU=Domain Controllers,DC=mikefrobbins,DC=com
DNSHostName       : dc01.mikefrobbins.com
Enabled           : True
Name              : DC01
ObjectClass       : computer
ObjectGUID        : c38da20c-a484-469d-ba4c-bab3fb71ae8e
SamAccountName    : DC01$
SID               : S-1-5-21-2989741381-570885089-3319121794-1001
UserPrincipalName :

DistinguishedName : CN=WEB01,CN=Computers,DC=mikefrobbins,DC=com
DNSHostName       : web01.mikefrobbins.com
Enabled           : True
Name              : WEB01
ObjectClass       : computer
ObjectGUID        : 33aa530e-1e31-40d8-8c78-76a18b673c33
SamAccountName    : WEB01$
SID               : S-1-5-21-2989741381-570885089-3319121794-1107
UserPrincipalName :

Wie Sie in den vorherigen Beispielen sehen können, akzeptiert der Parameter Identity für Get-ADComputer nur einen einzelnen Wert, wenn er über die Parametereingabe bereitgestellt wird, lässt aber mehrere Elemente zu, wenn die Eingabe über eine Pipelineeingabe bereitgestellt wird.

For

Eine for-Schleife wird durchlaufen, während eine bestimmte Bedingung erfüllt (true) ist. Die for-Schleife verwende ich nicht häufig, aber sie hat ihre Berechtigung.

for ($i = 1; $i -lt 5; $i++) {
  Write-Output "Sleeping for $i seconds"
  Start-Sleep -Seconds $i
}
Sleeping for 1 seconds
Sleeping for 2 seconds
Sleeping for 3 seconds
Sleeping for 4 seconds

Im vorherigen Beispiel wird die Schleife viermal durchlaufen, beginnend mit der Zahl 1, und wird fortgesetzt, solange die Zählervariable $i kleiner als 5 ist. Sie setzt für insgesamt 10 Sekunden aus.

Empfohlen

Es gibt zwei verschiedene do-Schleifen in PowerShell. Do Until wird ausgeführt, während die angegebene Bedingung nicht erfüllt (false) ist.

$number = Get-Random -Minimum 1 -Maximum 10
do {
  $guess = Read-Host -Prompt "What's your guess?"
  if ($guess -lt $number) {
    Write-Output 'Too low!'
  }
  elseif ($guess -gt $number) {
    Write-Output 'Too high!'
  }
}
until ($guess -eq $number)
What's your guess?: 1
Too low!
What's your guess?: 2
Too low!
What's your guess?: 3

Das vorherige Beispiel ist ein Zahlenspiel, das solange fortgesetzt wird, bis der von Ihnen geschätzte Wert mit der Zahl übereinstimmt, die das Cmdlet Get-Random generiert hat.

Do While ist genau das Gegenteil. Sie wird ausgeführt, solange die angegebene Bedingung als „true“ ausgewertet wird.

$number = Get-Random -Minimum 1 -Maximum 10
do {
  $guess = Read-Host -Prompt "What's your guess?"
  if ($guess -lt $number) {
    Write-Output 'Too low!'
  } elseif ($guess -gt $number) {
    Write-Output 'Too high!'
  }
}
while ($guess -ne $number)
What's your guess?: 1
Too low!
What's your guess?: 2
Too low!
What's your guess?: 3
Too low!
What's your guess?: 4

Dieselben Ergebnisse werden mit einer Do While-Schleife erzielt, indem die Testbedingung in „ungleich“ umgekehrt wird.

Do-Schleifen werden immer mindestens einmal ausgeführt, weil die Bedingung am Ende der Schleife ausgewertet wird.

While

Ähnlich wie bei der Do While-Schleife, wird eine While-Schleife ausgeführt, solange die angegebene Bedingung erfüllt (true) ist. Der Unterschied besteht jedoch darin, dass eine While-Schleife die Bedingung am Anfang der Schleife auswertet, bevor überhaupt Code ausgeführt wird. Somit wird sie gar nicht ausgeführt, wenn die Bedingung als „false“ ausgewertet wird.

$date = Get-Date -Date 'November 22'
while ($date.DayOfWeek -ne 'Thursday') {
  $date = $date.AddDays(1)
}
Write-Output $date
Thursday, November 23, 2017 12:00:00 AM

Im vorherigen Beispiel wird berechnet, an welchem Tag in den USA „Thanksgiving“ (Erntedankfest) ist. Dies ist immer am vierten Donnerstag im November. Die Schleife beginnt also mit dem 22. Tag im November und fügt einen Tag hinzu, solange der Wochentag nicht gleich Donnerstag ist. Wenn der 22. ein Donnerstag ist, wird die Schleife überhaupt nicht ausgeführt.

Break, Continue und Return

Break dient dem Unterbrechen einer Schleife. Es wird auch häufig mit der switch-Anweisung verwendet.

for ($i = 1; $i -lt 5; $i++) {
  Write-Output "Sleeping for $i seconds"
  Start-Sleep -Seconds $i
  break
}
Sleeping for 1 seconds

Die im vorherigen Beispiel gezeigte break-Anweisung bewirkt, dass die Schleife bei der ersten Iterationen beendet wird.

„Continue“ ist so konzipiert, dass zur nächsten Iteration einer Schleife gesprungen wird.

while ($i -lt 5) {
  $i += 1
  if ($i -eq 3) {
    continue
  }
  Write-Output $i
}
1
2
4
5

Das vorherige Beispiel gibt die Zahlen 1, 2, 4 und 5 aus. Es überspringt die Zahl 3 und fährt mit der nächsten Iteration der Schleife fort. Ähnlich wie bei break, unterbricht continue die Schleife, wenn auch nur für die aktuelle Iteration. Die Ausführung wird mit der nächsten Iteration fortgesetzt, anstatt die Schleife zu unterbrechen und zu beenden.

„Return“ ist so konzipiert, dass der vorhandene Bereich verlassen wird.

$number = 1..10
foreach ($n in $number) {
  if ($n -ge 4) {
    Return $n
  }
}
4

Beachten Sie, dass im vorherigen Beispiel das erste Ergebnis zurückgegeben und dann aus der Schleife beendet wird. Eine ausführlichere Erläuterung der Ergebnisausweisung finden Sie in einem meiner Blogartikel: "Die PowerShell gibt Schlüsselwort (keyword) zurück".

Zusammenfassung

In diesem Kapitel haben Sie die verschiedenen Arten von Schleifen kennengelernt, die in PowerShell vorhanden sind.

Überprüfung

  1. Worin besteht der Unterschied zwischen dem ForEach-Object-Cmdlet und dem foreach-Skriptkonstrukt?
  2. Worin besteht der primäre Vorteil bei der Verwendung einer While-Schleife anstelle einer „Do While“- oder „Do Until“-Schleife?
  3. Worin unterscheiden sich break- und continue-Anweisungen?