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
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
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
Lösung
Verwende PowerShells if
, elseif
und else
bedingte Anweisungen, um den Ausführungsfluss in deinem Skript zu steuern.
$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 Anweisungenif
oderelseif
davor den Werttrue
else
Anweisung-
Führt den nachfolgenden Skriptblock aus, wenn keine der Bedingungen in den Anweisungen
if
oderelseif
vor ihm den Werttrue
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
Lösung
Mit der PowerShell-Anweisung switch
kannst du eine große if
... elseif
... else
bedingte Anweisung einfacher darstellen.
$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
.
Siehe auch
4.4 Wiederholung von Operationen mit Schleifen
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
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
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.