Kapitel 4. Schleifen und Flusskontrolle

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

4.0 Einleitung

Wenn du anfängst, Skripte oder Befehle zu schreiben, die mit unbekannten Daten interagieren, werden die Konzepte von Schleifen und Flusskontrolle immer wichtiger.

Mit den PowerShell-Schleifenanweisungen und -Befehlen kannst du einen Vorgang (oder eine Reihe von Vorgängen) ausführen, ohne die Befehle selbst wiederholen zu müssen. Dazu gehört zum Beispiel, etwas eine bestimmte Anzahl von Malen auszuführen, jedes Element in einer Sammlung zu verarbeiten oder so lange zu arbeiten, bis eine bestimmte Bedingung erfüllt ist.

Mit den Flusssteuerungs- und Vergleichsanweisungen der PowerShell kannst du dein Skript oder deinen Befehl an unbekannte Daten anpassen. Sie ermöglichen es dir, Befehle basierend auf dem Wert dieser Daten auszuführen, Befehle basierend auf dem Wert dieser Daten zu überspringen, und vieles mehr.

Schleifen- und Flusssteuerungsanweisungen erweitern deinePowerShell-Werkzeugkiste um eine Vielzahl von Möglichkeiten.

4.1 Entscheidungen mit Vergleich und logischen Operatoren treffen

Problem

Du möchtest einige Daten mit anderen Daten vergleichen und auf der Grundlage dieses Vergleichs eine Entscheidung treffen.

Lösung

Verwende die logischen Operatoren der PowerShell, um Daten zu vergleichen und Entscheidungen zu treffen, die auf ihnen basieren:

Vergleichsoperatoren

-eq, -ne, -ge, -gt, -in, -notin, -lt, -le, -like, -notlike, -match, -notmatch,-contains, -notcontains, -is, -isnot

Logische Operatoren

-and, -or, -xor, -not

Eine detaillierte Beschreibung (und Beispiele) dieser Operatoren findest du unter "Vergleichsoperatoren".

Diskussion

Mit den logischen und Vergleichsoperatoren der PowerShell kannst du Daten vergleichen oder Daten auf eine Bedingung testen. Ein Operator vergleicht entweder zwei Daten (ein binärer Operator) oder testet ein Datenelement (ein unärer Operator). Alle Vergleichsoperatoren sind binäre Operatoren (sie vergleichen zwei Daten), ebenso wie die meisten logischen Operatoren. Der einzige unäre logische Operator ist der -not Operator, der das true/false Gegenteil der Daten, die er testet, zurückgibt.

Vergleichsoperatoren vergleichen zwei Daten und geben ein Ergebnis zurück, das von dem jeweiligen Vergleichsoperator abhängt. Du möchtest zum Beispiel prüfen, ob eine Sammlung mindestens eine bestimmte Anzahl von Elementen enthält:

PS > (dir).Count -ge 4
True

oder prüfen, ob eine Zeichenkette mit einem bestimmten regulären Ausdruck übereinstimmt:

PS > "Hello World" -match "H.*World"
True

Die meisten Vergleichsoperatoren passen sich auch an den Typ ihrer Eingabe an. Wenn du sie zum Beispiel auf einfache Daten wie eine Zeichenkette anwendest, ermitteln die Vergleichsoperatoren -like und -match, ob die Zeichenkette dem angegebenen Muster entspricht. Wenn du sie auf eine Sammlung einfacher Daten anwendest, geben dieselben Vergleichsoperatoren alle Elemente in dieser Sammlung zurück, die mit dem angegebenen Muster übereinstimmen.

Hinweis

Der -match Operator nimmt einen regulären Ausdruck als Argument. Eines der gebräuchlichsten Symbole für reguläre Ausdrücke ist das Zeichen $, das das Ende einer Zeile darstellt. Das Zeichen $ steht aber auch für den Beginn einer PowerShell-Variablen! Um zu verhindern, dass PowerShell die Zeichen als Sprachausdrücke oder Escape-Sequenzen interpretiert, setzt du die Zeichenfolge in einfache Anführungszeichen statt in doppelte Anführungszeichen:

PS > "Hello World" -match "Hello"
True
PS > "Hello World" -match 'Hello$'
False

Standardmäßig sind die Vergleichsoperatoren der PowerShell case-insensitive. Um die Versionen mit Unterscheidung der Groß- und Kleinschreibung zu verwenden, stellst du ihnen das Zeichen c voran:

-ceq, -cne, -cge, -cgt, -cin, -clt, -cle, -clike, -cnotlike,
-cmatch, -cnotmatch, -ccontains, -cnotcontains

Eine ausführliche Beschreibung der Vergleichsoperatoren, ihrer Gegenstücke, die Groß- und Kleinschreibung unterscheiden, und wie sie sich an die Eingabe anpassen, findest du unter "Vergleichsoperatoren".

Logische Operatoren kombinieren true oder false Anweisungen und geben ein Ergebnis zurück, das von dem jeweiligen logischen Operator abhängt. Du möchtest zum Beispiel prüfen, ob eine Zeichenkette mit dem von dir angegebenen Platzhaltermuster übereinstimmt und ob sie länger als eine bestimmte Anzahl von Zeichen ist:

PS > $data = "Hello World"
PS > ($data -like "*llo W*") -and ($data.Length -gt 10)
True
PS > ($data -like "*llo W*") -and ($data.Length -gt 20)
False

Einige der Vergleichsoperatoren enthalten sogar Aspekte der logischen Operatoren. Da die Verwendung des Gegenteils eines Vergleichs (z. B. -like) so häufig vorkommt, bietet die PowerShell Vergleichsoperatoren (z. B. -notlike), die es dir ersparen, den Operator -not explizit zu verwenden.

Eine detaillierte Beschreibung der einzelnen logischen Operatoren findest du unter "Vergleichsoperatoren".

Vergleichsoperatoren und logische Operatoren (in Kombination mit Flusssteuerungsanweisungen) bilden den Kern dessen, wie wir ein Skript oder einen Befehl schreiben, der sich an seine Daten und Eingaben anpasst.

Siehe auch "Bedingte Aussagen" für detaillierte Informationen über diese Aussagen.

Für weitere Informationen über die PowerShell-Operatoren gibst du ein Get-Help about_Operators.

4.2 Skriptablauf mit bedingten Anweisungen anpassen

Problem

Du möchtest die Bedingungen festlegen, unter denen die PowerShell Befehle oder Teile deines Skripts ausführt.

Lösung

Verwende PowerShells if, elseif und else bedingte Anweisungen, um den Ausführungsfluss in deinem Skript zu steuern.

Zum Beispiel:

$temperature = 90

if($temperature -le 0)
{
   "Balmy Canadian Summer"
}
elseif($temperature -le 32)
{
   "Freezing"
}
elseif($temperature -le 50)
{
   "Cold"
}
elseif($temperature -le 70)
{
   "Warm"
}
else
{
   "Hot"
}

Diskussion

Zu den bedingten Anweisungen gehören die folgenden:

if Anweisung

Führt den nachfolgenden Skriptblock aus, wenn seine Bedingung den Wert true

elseif Anweisung

Führt den nachfolgenden Skriptblock aus, wenn seine Bedingung true ergibt und keine der Bedingungen in den Anweisungen if oder elseif davor den Wert true

else Anweisung

Führt den nachfolgenden Skriptblock aus, wenn keine der Bedingungen in den Anweisungen if oder elseif vor ihm den Wert true

Unter sind bedingte Anweisungen nicht nur für den Skriptkontrollfluss nützlich, sondern auch, um einer Variablen Daten zuzuweisen. Die PowerShell macht dies sehr einfach, indem sie es dir ermöglicht, die Ergebnisse einer bedingten Anweisung direkt einer Variablen zuzuweisen:

$result = if(Get-Process -Name notepad) { "Running" } else { "Not running" }

Für sehr einfache bedingte Anweisungen wie diese kannst du auch den ternären Operator der PowerShell verwenden:

$result = (Get-Process -Name notepad*) ? "Running" : "Not running"

Für weitere Informationen über diese Flusssteuerungsanweisungen gibst du ein Get-Help about_If.

4.3 Verwalten großer bedingter Anweisungen mit Schaltern

Problem

Du willst einen einfacheren oder kompakteren Weg finden, um eine große if... elseif... else bedingte Anweisung darzustellen.

Lösung

Mit der PowerShell-Anweisung switch kannst du eine große if... elseif... else bedingte Anweisung einfacher darstellen.

Zum Beispiel:

$temperature = 20

switch($temperature)
{
   { $_ -lt 32 }   { "Below Freezing"; break }
   32              { "Exactly Freezing"; break }
   { $_ -le 50 }   { "Cold"; break }
   { $_ -le 70 }   { "Warm"; break }
   default         { "Hot" }
}

Diskussion

Mit der Anweisung switch von PowerShell kannst du die Eingabe ganz einfach gegen eine große Anzahl von Vergleichen testen. Die Anweisung switch unterstützt mehrere Optionen, mit denen du konfigurieren kannst, wie PowerShell die Eingabe mit den Bedingungen vergleicht - zum Beispiel mit einem Platzhalter, einem regulären Ausdruck oder sogar einem beliebigen Skriptblock. Da das Durchsuchen des Textes in einer Datei eine so häufige Aufgabe ist,unterstützt die PowerShell-Anweisung switch dies direkt. Durch diese Ergänzungen sind die PowerShell switch Anweisungen viel mächtiger als die in C und C++.

Unter findest du ein weiteres Beispiel für die switch Anweisung in Aktion, um die SKU des aktuellen Betriebssystems zu ermitteln. Läuft das Skript zum Beispiel auf Windows 7 Ultimate? Windows Server Cluster Edition? Mit dem Cmdlet Get-CimInstance kannst du die SKU des Betriebssystems ermitteln, aber leider gibt es das Ergebnis als einfache Zahl zurück. Mit der Anweisung switch kannst du diese Zahlen auf der Grundlage der offiziellen Dokumentation auf ihre englischen Entsprechungen übertragen:

##############################################################################
##
## Get-OperatingSystemSku
##
## From PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################

<#

.SYNOPSIS

Gets the sku information for the current operating system

.EXAMPLE

PS > Get-OperatingSystemSku
Professional with Media Center

#>

param($Sku =
    (Get-CimInstance Win32_OperatingSystem).OperatingSystemSku)

Set-StrictMode -Version 3

switch ($Sku)
{
    0   { "An unknown product"; break; }
    1   { "Ultimate"; break; }
    2   { "Home Basic"; break; }
    3   { "Home Premium"; break; }
    4   { "Enterprise"; break; }
    5   { "Home Basic N"; break; }
    6   { "Business"; break; }
    7   { "Server Standard"; break; }
    8   { "Server Datacenter (full installation)"; break; }
    9   { "Windows Small Business Server"; break; }
    10  { "Server Enterprise (full installation)"; break; }
    11  { "Starter"; break; }
    12  { "Server Datacenter (core installation)"; break; }
    13  { "Server Standard (core installation)"; break; }
    14  { "Server Enterprise (core installation)"; break; }
    15  { "Server Enterprise for Itanium-based Systems"; break; }
    16  { "Business N"; break; }
    17  { "Web Server (full installation)"; break; }
    18  { "HPC Edition"; break; }
    19  { "Windows Storage Server 2008 R2 Essentials"; break; }
    20  { "Storage Server Express"; break; }
    21  { "Storage Server Standard"; break; }
    22  { "Storage Server Workgroup"; break; }
    23  { "Storage Server Enterprise"; break; }
    24  { "Windows Server 2008 for Windows Essential Server Solutions"; break; }
    25  { "Small Business Server Premium"; break; }
    26  { "Home Premium N"; break; }
    27  { "Enterprise N"; break; }
    28  { "Ultimate N"; break; }
    29  { "Web Server (core installation)"; break; }
    30  { "Windows Essential Business Server Management Server"; break; }
    31  { "Windows Essential Business Server Security Server"; break; }
    32  { "Windows Essential Business Server Messaging Server"; break; }
    33  { "Server Foundation"; break; }
    34  { "Windows Home Server 2011"; break; }
    35  { "Windows Server 2008 without Hyper-V for Windows Essential Server"; break; }
    36  { "Server Standard without Hyper-V"; break; }
    37  { "Server Datacenter without Hyper-V (full installation)"; break; }
    38  { "Server Enterprise without Hyper-V (full installation)"; break; }
    39  { "Server Datacenter without Hyper-V (core installation)"; break; }
    40  { "Server Standard without Hyper-V (core installation)"; break; }
    41  { "Server Enterprise without Hyper-V (core installation)"; break; }
    42  { "Microsoft Hyper-V Server"; break; }
    43  { "Storage Server Express (core installation)"; break; }
    44  { "Storage Server Standard (core installation)"; break; }
    45  { "Storage Server Workgroup (core installation)"; break; }
    46  { "Storage Server Enterprise (core installation)"; break; }
    46  { "Storage Server Enterprise (core installation)"; break; }
    47  { "Starter N"; break; }
    48  { "Professional"; break; }
    49  { "Professional N"; break; }
    50  { "Windows Small Business Server 2011 Essentials"; break; }
    51  { "Server For SB Solutions"; break; }
    52  { "Server Solutions Premium"; break; }
    53  { "Server Solutions Premium (core installation)"; break; }
    54  { "Server For SB Solutions EM"; break; }
    55  { "Server For SB Solutions EM"; break; }
    56  { "Windows MultiPoint Server"; break; }
    59  { "Windows Essential Server Solution Management"; break; }
    60  { "Windows Essential Server Solution Additional"; break; }
    61  { "Windows Essential Server Solution Management SVC"; break; }
    62  { "Windows Essential Server Solution Additional SVC"; break; }
    63  { "Small Business Server Premium (core installation)"; break; }
    64  { "Server Hyper Core V"; break; }
    72  { "Server Enterprise (evaluation installation)"; break; }
    76  { "Windows MultiPoint Server Standard (full installation)"; break; }
    77  { "Windows MultiPoint Server Premium (full installation)"; break; }
    79  { "Server Standard (evaluation installation)"; break; }
    80  { "Server Datacenter (evaluation installation)"; break; }
    84  { "Enterprise N (evaluation installation)"; break; }
    95  { "Storage Server Workgroup (evaluation installation)"; break; }
    96  { "Storage Server Standard (evaluation installation)"; break; }
    98  { "Windows 8 N"; break; }
    99  { "Windows 8 China"; break; }
    100 { "Windows 8 Single Language"; break; }
    101 { "Windows 8"; break; }
    103 { "Professional with Media Center"; break; }

    default {"UNKNOWN: " + $SKU }
}

Obwohl sie dazu dient, große bedingte Anweisungen sauberer auszudrücken, funktioniert eine switch Anweisung ähnlich wie eine große Folge von if Anweisungen, im Gegensatz zu einer großen Folge von if... elseif... elseif... else Anweisungen. Die von dir eingegebenen Daten werden von der PowerShell mit jedem der Vergleiche in der Anweisung switch verglichen. Wenn der Vergleich den Wert true ergibt, führt PowerShell den darauf folgenden Skriptblock aus. Wenn dieser Skriptblock keine break Anweisung enthält, fährtPowerShell mit der Auswertung der folgenden Vergleiche fort.

Weitere Informationen über die PowerShell-Anweisung switch finden Sie unter "Bedingte Anweisungen" oder geben Sie Get-Help about_Switch.

4.4 Wiederholung von Operationen mit Schleifen

Problem

Du willst denselben Codeblock mehr als einmal ausführen.

Lösung

Verwende eine der PowerShell-Schleifenanweisungen (for, foreach, while und do) oder das PowerShell-Cmdlet ForEach-Object, um einen Befehl oder Skriptblock mehr als einmal auszuführen. Eine detaillierte Beschreibung dieser Schleifenanweisungen findest du unter "Schleifenanweisungen". Ein Beispiel:

for Schleife
for($counter = 1; $counter -le 10; $counter++)
{
  "Loop number $counter"
}
foreach Schleife
foreach($file in dir)
{
  "File length: " + $file.Length
}
ForEach-Object cmdlet
Get-ChildItem | ForEach-Object { "File length: " + $_.Length }
while Schleife
$response = ""
while($response -ne "QUIT")
{
  $response = Read-Host "Type something"
}
do..while Schleife
$response = ""
do
{
  $response = Read-Host "Type something"
} while($response -ne "QUIT")
do..until Schleife
$response = ""
do
{
  $response = Read-Host "Type something"
} until($response -eq "QUIT")

Diskussion

Obwohl jede der Schleifenanweisungen so geschrieben werden kann, dass sie funktional äquivalent zu den anderen ist, führt jede von ihnen zu bestimmten Problemen.

Du verwendest eine for Schleife normalerweise, wenn du eine Operation genau eine bestimmte Anzahl von Malen ausführen musst. Weil das so häufig vorkommt, wird sie oft auch als gezählte for Schleife bezeichnet.

Normalerweise verwendest du eine foreach Schleife, wenn du eine Sammlung von Objekten hast und jedes Element in dieser Sammlung besuchen willst. Wenn du noch nicht die gesamte Sammlung im Speicher hast (wie in der dir Sammlung aus dem foreach Beispiel), ist das Cmdlet ForEach-Object normalerweise eine effizientere Alternative.

Im Gegensatz zur Schleife foreach kannst du mit dem Cmdlet ForEach-Object jedes Element in der Sammlung so verarbeiten , wie PowerShell es generiert. Das ist ein wichtiger Unterschied: Wenn du PowerShell bittest, die gesamte Ausgabe eines großen Befehls (wie Get-Content hugefile.txt) in einer foreach Schleife zu sammeln, kann dein System leicht ausbremsen.

Wie pipeline-orientierte Funktionen kannst du mit dem Cmdlet ForEach-Object Befehle definieren, die vor Beginn der Schleife, während der Schleife und nach Abschluss der Schleife ausgeführt werden:

PS > "a","b","c" | ForEach-Object `
    -Begin { "Starting"; $counter = 0 } `
    -Process { "Processing $_"; $counter++ } `
    -End { "Finishing: $counter" }

Starting
Processing a
Processing b
Processing c
Finishing: 3
Tipp

Um mehrere Operationen in deiner Schleife gleichzeitig aufzurufen, verwende den Schalter -parallel von ForEach-Object. Weitere Informationen findest du in Rezept 4.5.


Die Schleifen while und do..while sind insofern ähnlich, als sie die Schleife so lange ausführen, wie die Bedingung true lautet. Eine while -Schleife prüft dies vor der Ausführung deines Skriptblocks, während eine do..while -Schleife die Bedingung nach der Ausführung deines Skriptblocks prüft. Eine do..until -Schleife ist genau wie eine do..while -Schleife, nur dass sie beendet wird, wenn ihre Bedingung $true ergibt, und nicht, wenn ihre Bedingung $false ergibt.

Eine detaillierte Beschreibung dieser Schleifenanweisungen finden Sie unter "Schleifenanweisungen" oder geben Sie Get-Help about_For, Get-Help about_Foreach, Get-Help about_While, oder Get-Help about_Do.

4.5 Zeitaufwendige Aktionen parallel bearbeiten

Problem

Du hast eine Reihe von Daten oder Aktionen, die du gleichzeitig ausführen möchtest.

Lösung

Verwende den Schalter -parallel des Cmdlets ForEach-Object:

PS > Measure-Command { 1..5 | ForEach-Object { Start-Sleep -Seconds 5 } }

(...)
TotalSeconds      : 25.0247856
(...)

PS > Measure-Command { 1..5 | ForEach-Object -parallel { Start-Sleep -Seconds 5 } }

(...)
TotalSeconds      : 5.1354752
(...)

Diskussion

In der PowerShell gibt es Situationen, in denen du einen langlaufenden Vorgang erheblich beschleunigen kannst, indem du Teile davon gleichzeitig ausführst. Perfekte Gelegenheiten dafür sind Szenarien, in denen dein Skript die meiste Zeit auf Netzwerkressourcen (z. B. das Herunterladen von Dateien oder Webseiten) oder langsame Vorgänge (z. B. den Neustart einer Reihe langsamer Dienste) wartet.

In diesen Szenarien kannst du den Parameter -parallel von ForEach-Object verwenden, um diese Aktionen gleichzeitig auszuführen. Im Verborgenen verwendet die PowerShell Hintergrundaufträge, um jeden Zweig auszuführen. Sie begrenzt die Anzahl der gleichzeitig ausgeführten Zweige auf den Wert, den du im Parameter -ThrottleLimit angibst, wobei der Standardwert 5 ist.

Hinweis

Wenn der Grund dafür, dass du mehrere Befehle parallel ausführen willst, darin liegt, dass du eine Aufgabe schnell auf einer großen Anzahl von Maschinen erledigen willst, solltest du stattdessen Invoke-Command verwenden. Weitere Informationen findest du in Rezept 29.5.

Da PowerShell diese Zweige als Hintergrundaufträge ausführt, musst du entweder die $USING Syntax verwenden, um externe Variablen in diesen Hintergrundauftrag zu bringen (PowerShell bringt standardmäßig $_ mit) oder die Variablen im Parameter -ArgumentList angeben. Beispiel:

PS > $greeting = "World"
PS > 1..5 | ForEach-Object -parallel { "Hello $greeting" }
Hello
Hello
Hello
Hello
Hello

PS > 1..5 | ForEach-Object -parallel { "Hello $USING:greeting" }
Hello World
Hello World
Hello World
Hello World
Hello World

PowerShell führt diese Aufträge im Hintergrund in deinem PowerShell-Hauptprozess aus, so dass du auf Eingaben als Live-Instanzen reagieren kannst:

$processes = 1..10 | ForEach-Object { Start-Process notepad -PassThru }
$processes | ForEach-Object -parallel { $_.Kill() }

Wenn du die Zweige deiner parallelen Schleife an deine Hauptshell zurückmelden musst, empfiehlt es sich, dies durch die Ausgabe von Skriptblöcken zu erreichen und die Ergebnisse dann von deiner Hauptshell verarbeiten zu lassen. Es ist verlockend, dies mit lebenden Objekten zu tun, aber sei dir bewusst, dass der Weg dorthin tückisch und schwierig ist. Nehmen wir ein einfaches Beispiel - eine parallele Operation, um einen Zähler zu erhöhen.

Auf den ersten Blick mag es so aussehen, als ob du sie benutzen solltest:

$counter = 0
1..10 | ForEach-Object -parallel {
    $myCounter = $USING:counter
    $myCounter = $myCounter + 1
}

Wenn du jedoch in der PowerShell $counter = $counter + 1 eingibst, aktualisiert die PowerShell die Variable $counter im aktuellen Bereich. Wenn du ein Objekt von einem Hintergrundauftrag aus ändern willst, musst du das tun, indem du eine Eigenschaft eines aktiven Objekts festlegst, anstatt zu versuchen, das Objekt zu ersetzen. Zum Glück gibt es in der PowerShell einen Typ namens [ref] für diese Art von Szenario:

$counter = [ref] 0
1..10 | ForEach-Object -parallel {
    $myCounter = $USING:counter
    $myCounter.Value = $myCounter.Value + 1
}

Anfänglich scheint das zu funktionieren:

PS > $counter

Value
-----
   10

Jetzt, wo wir stolz auf uns sind, lass uns das wirklich parallel machen:

$counter = [ref] 0
1..10000 | ForEach-Object -throttlelimit 100 -parallel {
    $myCounter = $USING:counter
    $myCounter.Value = $myCounter.Value + 1
}
PS > $counter

Value
-----
 9992

Ups! Da wir dies mit massiver Parallelität gemacht haben, kann sich $myCounter.Value jederzeit in den Teilen der Pipeline ändern, in denen PowerShell $myCounter.Value = $myCounter.Value + 1 ausgeführt wird. Dies wird als Race Condition bezeichnet und ist in jeder Sprache üblich, in der Code aus mehreren Blöcken gleichzeitig ausgeführt wird. Um die seltsamen Zwischenzustände loszuwerden, müssen wir die Interlocked Increment-Klasse aus dem .Net Framework verwenden:

$counter = [ref] 0
1..10000 | ForEach-Object -throttlelimit 100 -parallel {
    $myCounter = $USING:counter
    $null = [Threading.Interlocked]::Increment($myCounter)
}

Was uns korrekt ergibt:

PS > $counter

Value
-----
10000

Diese Probleme sind hartnäckig und machen selbst professionellen Programmierern regelmäßig zu schaffen. Die bewährte Methode, um diese Probleme zu lösen, besteht darin, den Bereich ganz zu vermeiden, indem keine gemeinsamen Zustände verarbeitet oder bearbeitet werden.

4.6 Hinzufügen einer Pause oder Verzögerung

Problem

Du möchtest dein Skript oder deinen Befehl unterbrechen oder verzögern.

Lösung

Um zu pausieren, bis der Benutzer die Eingabetaste drückt, verwende den Befehl pause:

PS > pause
Press Enter to continue...:

Um anzuhalten, bis der Benutzer eine Taste drückt, verwende die Methode ReadKey() für das Objekt $host:

PS > $host.UI.RawUI.ReadKey()

Um ein Skript für eine bestimmte Zeitspanne anzuhalten, verwende das Cmdlet Start-Sleep:

PS > Start-Sleep 5
PS > Start-Sleep -Milliseconds 300

Diskussion

Wenn du dein Skript anhalten willst, bis der Benutzer eine Taste drückt oder für eine bestimmte Zeit, sind pause und Start-Sleep die beiden Cmdlets, die du am ehesten verwenden wirst.

Hinweis

Wenn du Benutzereingaben abrufen möchtest, anstatt nur zu pausieren, kannst du mit dem Cmdlet Read-Host Eingaben vom Benutzer lesen. Weitere Informationen findest du in Rezept 13.1.

In anderen Situationen möchtest du vielleicht eine Schleife in deinem Skript schreiben, die mit einer konstanten Geschwindigkeit abläuft - zum Beispiel einmal pro Minute oder 30 Mal pro Sekunde. Das ist in der Regel eine schwierige Aufgabe, denn die Befehle in der Schleife können viel Zeit in Anspruch nehmen oder sogar unregelmäßig ablaufen.

In der Vergangenheit litten viele Computerspiele darunter, dass dieses Problem falsch gelöst wurde. Um die Spielgeschwindigkeit zu kontrollieren, fügten die Spieleentwickler Befehle hinzu, um ihr Spiel zu verlangsamen. Nach langem Tüfteln stellen die Entwickler zum Beispiel fest, dass das Spiel auf einem normalen Rechner richtig läuft, wenn sie den Computer bei jeder Aktualisierung des Bildschirms bis 1 Million zählen lassen. Leider hängt die Geschwindigkeit dieser Befehle (wie das Zählen) stark von der Geschwindigkeit des Computers ab. Da ein schneller Computer viel schneller bis 1 Million zählen kann als ein langsamer Computer, läuft das Spiel auf schnelleren Computern viel schneller (oft so schnell, dass es unverständlich ist)!

Um zu erreichen, dass deine Schleife mit gleichmäßiger Geschwindigkeit läuft, kannst du messen, wie lange die Befehle in einer Schleife brauchen, und dann die verbleibende Zeit verzögern, wie in Beispiel 4-1 gezeigt.

Beispiel 4-1. Eine Schleife mit konstanter Geschwindigkeit laufen lassen
$loopDelayMilliseconds = 650
while($true)
{
   $startTime = Get-Date

   ## Do commands here
   "Executing"

   $endTime = Get-Date
   $loopLength = ($endTime - $startTime).TotalMilliseconds
   $timeRemaining = $loopDelayMilliseconds - $loopLength

   if($timeRemaining -gt 0)
   {
      Start-Sleep -Milliseconds $timeRemaining
   }
}

Für weitere Informationen über das Cmdlet Start-Sleep, gibst du ein Get-Help Start-Sleep.

Get PowerShell Kochbuch, 4. Auflage now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.