Alles, was Sie schon immer über die switch-Anweisung wissen wollten
Wie viele andere Sprachen bietet PowerShell Befehle zur Ablaufsteuerung der Ausführung innerhalb Ihrer Skripts. Eine dieser Anweisungen ist die switch-Anweisung in PowerShell mit Features, die es in anderen Sprachen nicht gibt. Heute beschäftigen wir uns eingehend mit dem Arbeiten mit der PowerShell-Anweisung switch
.
Hinweis
Die Originalversion dieses Artikels erschien ursprünglich in einem Blog von @KevinMarquette. Das PowerShell-Team dankt Kevin Marquette, dass er diesen Inhalt mit uns teilt. Weitere Informationen finden Sie in seinem Blog auf PowerShellExplained.com.
Die Anweisung if
Eine der ersten Anweisungen, die Sie kennenlernen, ist die if
-Anweisung. Sie können einen Skriptblock ausführen, wenn eine Anweisung $true
ist.
if ( Test-Path $Path )
{
Remove-Item $Path
}
Eine viel kompliziertere Logik ist möglich, wenn Sie die Anweisungen elseif
und else
nutzen. Hier ist ein Beispiel, bei dem ich einen numerischen Wert für einen Wochentag habe und ich den Namen als Zeichenfolge abrufen möchte.
$day = 3
if ( $day -eq 0 ) { $result = 'Sunday' }
elseif ( $day -eq 1 ) { $result = 'Monday' }
elseif ( $day -eq 2 ) { $result = 'Tuesday' }
elseif ( $day -eq 3 ) { $result = 'Wednesday' }
elseif ( $day -eq 4 ) { $result = 'Thursday' }
elseif ( $day -eq 5 ) { $result = 'Friday' }
elseif ( $day -eq 6 ) { $result = 'Saturday' }
$result
Wednesday
Es stellt sich heraus, dass dies ein weit verbreitetes Muster ist und dass es viele Möglichkeiten gibt, damit umzugehen. Eine davon ist mit einer switch
-Anweisung.
switch-Anweisung
Mit der switch
-Anweisung können Sie eine Variable und eine Liste möglicher Werte angeben. Wenn der Wert mit der Variablen übereinstimmt, wird der zugehörige Skriptblock ausgeführt.
$day = 3
switch ( $day )
{
0 { $result = 'Sunday' }
1 { $result = 'Monday' }
2 { $result = 'Tuesday' }
3 { $result = 'Wednesday' }
4 { $result = 'Thursday' }
5 { $result = 'Friday' }
6 { $result = 'Saturday' }
}
$result
'Wednesday'
In diesem Beispiel entspricht der Wert von $day
einem der numerischen Werte. Dann wird $result
der richtige Name zugewiesen. Wir weisen in diesem Beispiel nur eine Variable zu, aber jeder beliebige PowerShell-Befehl kann in diesen Skriptblöcken ausgeführt werden.
Zuweisen zu einer Variablen
Dieses letzte Beispiel kann auf andere Weise geschrieben werden.
$result = switch ( $day )
{
0 { 'Sunday' }
1 { 'Monday' }
2 { 'Tuesday' }
3 { 'Wednesday' }
4 { 'Thursday' }
5 { 'Friday' }
6 { 'Saturday' }
}
Wir legen den Wert in der PowerShell-Pipeline ab und weisen ihn $result
zu. Dasselbe lässt sich mit den Anweisungen if
und foreach
realisieren.
Standard
Wir können das Schlüsselwort default
nutzen, um zu bestimmen, was geschehen soll, wenn es keine Übereinstimmung gibt.
$result = switch ( $day )
{
0 { 'Sunday' }
# ...
6 { 'Saturday' }
default { 'Unknown' }
}
Hier geben wir im Standardfall den Wert Unknown
zurück.
Zeichenfolgen
In den letzten Beispielen habe ich Zahlen auf Übereinstimmung abgeglichen, aber Sie können auch Zeichenfolgen abgleichen.
$item = 'Role'
switch ( $item )
{
Component
{
'is a component'
}
Role
{
'is a role'
}
Location
{
'is a location'
}
}
is a role
Ich habe beschlossen, die Abgleiche auf Component
, Role
und Location
hier nicht in Anführungszeichen zu setzen, um hervorzuheben, dass sie optional sind. switch
behandelt diese in den meisten Fällen als Zeichenfolge.
Arrays
Eines der coolen Features der PowerShell-Anweisung switch
ist die Handhabung von Arrays. Wenn Sie an switch
ein Array übergeben, wird jedes Element in dieser Sammlung verarbeitet.
$roles = @('WEB','Database')
switch ( $roles ) {
'Database' { 'Configure SQL' }
'WEB' { 'Configure IIS' }
'FileServer' { 'Configure Share' }
}
Configure IIS
Configure SQL
Wenn Ihr Array sich wiederholende Elemente enthält, werden diese im entsprechenden Abschnitt mehrfach abgeglichen.
PSItem
Sie können $PSItem
oder $_
nutzen, um auf das aktuelle Element zu verweisen, das verarbeitet wurde. Wenn wir einen einfachen Abgleich vornehmen, ist $PSItem
der Wert, den wir auf Übereinstimmung abgleichen. Im nächsten Abschnitt möchte ich einige komplexere Abgleiche durchführen, bei denen diese Variable zum Einsatz kommt.
Parameter
Ein besonderes Merkmal der PowerShell-Anweisung switch
ist, dass sie über eine Reihe von switch-Parameter verfügt, die ihre Funktionsweise beeinflussen.
-CaseSensitive
Standardmäßig wird beim Abgleich Groß-/Kleinschreibung nicht beachtet. Wenn Sie Groß-/Kleinschreibung beachten müssen, können Sie -CaseSensitive
verwenden. Dies kann in Kombination mit den anderen Schalterparametern genutzt werden.
-Wildcard
Mit dem Schalter -wildcard
können wir Platzhalterunterstützung aktivieren. Dabei wird für jeden Abgleich auf Übereinstimmung die gleiche Platzhalterlogik wie beim Operator -like
genutzt.
$Message = 'Warning, out of disk space'
switch -Wildcard ( $message )
{
'Error*'
{
Write-Error -Message $Message
}
'Warning*'
{
Write-Warning -Message $Message
}
default
{
Write-Information $message
}
}
WARNING: Warning, out of disk space
Hier verarbeiten wir eine Nachricht und geben sie dann je nach Inhalt in verschiedenen Datenströmen aus.
-Regex
Die switch-Anweisung unterstützt Übereinstimmungen mit regulären Ausdrücken (RegEx) ebenso wie Platzhalter.
switch -Regex ( $message )
{
'^Error'
{
Write-Error -Message $Message
}
'^Warning'
{
Write-Warning -Message $Message
}
default
{
Write-Information $message
}
}
Weitere Beispiele für die Nutzung regulärer Ausdrücke finden Sie in einem anderen Artikel von mir: The many ways to use regex (Die vielfältigen Verwendungsmöglichkeiten von regulären Ausdrücken)
-File
Ein wenig bekanntes Merkmal der switch-Anweisung ist, dass sie eine Datei mit dem Parameter -File
verarbeiten kann. Sie verwenden -file
mit einem Pfad zu einer Datei, anstatt dafür einen variablen Ausdruck anzugeben.
switch -Wildcard -File $path
{
'Error*'
{
Write-Error -Message $PSItem
}
'Warning*'
{
Write-Warning -Message $PSItem
}
default
{
Write-Output $PSItem
}
}
Dies funktioniert wie die Verarbeitung eines Arrays. In diesem Beispiel kombiniere ich dies mit dem Platzhalterabgleich und nutze $PSItem
. Damit ließe sich eine Protokolldatei verarbeiten und in Abhängigkeit von Übereinstimmungen mit regulären Ausdrücken in Warn- und Fehlermeldungen umwandeln.
Details zur erweiterten Verarbeitung
Jetzt, da Ihnen all diese dokumentierten Merkmale bekannt sind, können wir sie im Rahmen einer komplexeren Verarbeitung nutzen.
Ausdrücke
switch
kann für einen Ausdruck statt für eine Variable gelten.
switch ( ( Get-Service | Where status -eq 'running' ).name ) {...}
Das Ergebnis des Ausdrucks ist der für den Abgleich auf Übereinstimmung genutzte Wert.
Mehrere Übereinstimmungen
Möglicherweise haben Sie dies bereits bemerkt, aber eine switch
-Anweisung kann mit mehreren Bedingungen übereinstimmen. Dies gilt insbesondere, wenn der Abgleich mit -wildcard
oder -regex
erfolgt. Sie können dieselbe Bedingung mehrmals hinzufügen, und alle werden ausgelöst.
switch ( 'Word' )
{
'word' { 'lower case word match' }
'Word' { 'mixed case word match' }
'WORD' { 'upper case word match' }
}
lower case word match
mixed case word match
upper case word match
Alle drei dieser Anweisungen werden ausgelöst. Dies zeigt, dass jede Bedingung (der Reihe nach) geprüft wird. Dies gilt für die Verarbeitung von Arrays, bei der jedes Element jede Bedingung prüft.
Continue
Normalerweise würde ich an dieser Stelle die Anweisung break
einführen, aber es ist besser, wenn wir zunächst erfahren, wie continue
verwendet wird. Ebenso wie bei einer foreach
-Schleife fährt continue
mit dem nächsten Element in der Sammlung fort oder verlässt die switch
-Anweisung, wenn es keine weiteren Elemente mehr gibt. Wir können das letzte Beispiel mit continue-Anweisungen so umschreiben, dass nur eine Anweisung ausgeführt wird.
switch ( 'Word' )
{
'word'
{
'lower case word match'
continue
}
'Word'
{
'mixed case word match'
continue
}
'WORD'
{
'upper case word match'
continue
}
}
lower case word match
Anstatt alle drei Elemente abzugleichen, wird das erste abgeglichen, und die switch-Anweisung fährt mit dem nächsten Wert fort. Da es keine weiteren Werte zu verarbeiten gibt, endet die switch-Anweisung. Dieses nächste Beispiel zeigt, wie ein Platzhalter mit mehreren Elementen übereinstimmen kann.
switch -Wildcard -File $path
{
'*Error*'
{
Write-Error -Message $PSItem
continue
}
'*Warning*'
{
Write-Warning -Message $PSItem
continue
}
default
{
Write-Output $PSItem
}
}
Da eine Zeile in der Eingabedatei sowohl das Wort Error
als auch das Wort Warning
enthalten könnte, möchten wir nur, dass das erste ausgeführt wird, und dann mit der Verarbeitung der Datei fortfahren.
Break
Eine break
-Anweisung beendet die switch-Anweisung. Dies ist das gleiche Verhalten wie bei continue
für einzelne Werte. Der Unterschied zeigt sich bei der Verarbeitung eines Arrays. break
beendet die gesamte Verarbeitung in der switch-Anweisung, während continue
zum nächsten Element übergeht.
$Messages = @(
'Downloading update'
'Ran into errors downloading file'
'Error: out of disk space'
'Sending email'
'...'
)
switch -Wildcard ($Messages)
{
'Error*'
{
Write-Error -Message $PSItem
break
}
'*Error*'
{
Write-Warning -Message $PSItem
continue
}
'*Warning*'
{
Write-Warning -Message $PSItem
continue
}
default
{
Write-Output $PSItem
}
}
Downloading update
WARNING: Ran into errors downloading file
write-error -message $PSItem : Error: out of disk space
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException
Wenn wir in diesem Fall auf Zeilen stoßen, die mit Error
beginnen, erhalten wir einen Fehler, woraufhin die switch-Anweisung endet.
Diese Aufgabe erledigt diese break
-Anweisung für uns. Wenn wir Error
innerhalb der Zeichenfolge und nicht nur am Anfang finden, schreiben wir dies als Warnung. Das Gleiche machen wir für Warning
. Es ist möglich, dass eine Zeile die Wörter Error
und Warning
enthalten kann, aber wir müssen nur eines davon verarbeiten. Diese Aufgabe erledigt die continue
-Anweisung für uns.
break-Bezeichnungen
Die switch
-Anweisung unterstützt break/continue
-Bezeichnungen wie foreach
.
:filelist foreach($path in $logs)
{
:logFile switch -Wildcard -File $path
{
'Error*'
{
Write-Error -Message $PSItem
break filelist
}
'Warning*'
{
Write-Error -Message $PSItem
break logFile
}
default
{
Write-Output $PSItem
}
}
}
Mir persönlich gefallen break-Bezeichnungen nicht, aber ich möchte auf sie hinweisen, weil sie verwirrend sind, wenn Sie sie noch nie zuvor gesehen haben. Wenn Sie mehrere geschachtelte switch
- oder foreach
-Anweisungen haben, möchten Sie möglicherweise bei mehr als nur dem innersten Element unterbrechen. Sie können einer switch
-Anweisung eine Bezeichnung zuweisen, die das Ziel Ihrer break
-Anweisung sein kann.
Enum
Seit PowerShell 5.0 stehen uns Enumerationen zur Verfügung, die wir in einer switch-Anweisung nutzen können.
enum Context {
Component
Role
Location
}
$item = [Context]::Role
switch ( $item )
{
Component
{
'is a component'
}
Role
{
'is a role'
}
Location
{
'is a location'
}
}
is a role
Wenn Sie alles als stark typisierte Enumerationen beibehalten möchten, können Sie sie in Klammern setzen.
switch ($item )
{
([Context]::Component)
{
'is a component'
}
([Context]::Role)
{
'is a role'
}
([Context]::Location)
{
'is a location'
}
}
Die Klammern werden hier benötigt, damit die switch-Anweisung den Wert [Context]::Location
nicht als Literalzeichenfolge behandelt.
ScriptBlock
Bei Bedarf können wir einen Skriptblock verwenden, um die Auswertung für eine Übereinstimmung vorzunehmen.
$age = 37
switch ( $age )
{
{$PSItem -le 18}
{
'child'
}
{$PSItem -gt 18}
{
'adult'
}
}
'adult'
Dies erhöht die Komplexität und kann Ihre switch
-Anweisung schwer lesbar machen. In den meisten Fällen, in denen Sie so etwas verwenden würden, wäre es besser, mit den Anweisungen if
und elseif
zu arbeiten. Ich würde dies erwägen, wenn ich bereits eine große switch-Anweisung hätte und ich zwei Elemente bräuchte, um auf denselben Auswertungsblock zu treffen.
Ein Punkt, der meiner Meinung nach zur Lesbarkeit beiträgt, ist das Setzen des Skriptblocks in Klammern.
switch ( $age )
{
({$PSItem -le 18})
{
'child'
}
({$PSItem -gt 18})
{
'adult'
}
}
Er wird weiterhin auf die gleiche Weise ausgeführt und bietet beim schnellen Betrachten eine bessere visuelle Abgrenzung.
$matches für reguläre Ausdrücke
Wir müssen reguläre Ausdrücke wieder aufgreifen, um etwas anzusprechen, das nicht sofort offensichtlich ist. Bei Verwendung regulärer Ausdrücke wird die Variable $matches
aufgefüllt. Ich gehe mehr auf die Verwendung von $matches
ein, wenn ich auf The many ways to use regex eingehe. Hier ist ein kurzes Beispiel, um das Ganze mit benannten Übereinstimmungen in Aktion zu zeigen.
$message = 'my ssn is 123-23-3456 and credit card: 1234-5678-1234-5678'
switch -regex ($message)
{
'(?<SSN>\d\d\d-\d\d-\d\d\d\d)'
{
Write-Warning "message contains a SSN: $($matches.SSN)"
}
'(?<CC>\d\d\d\d-\d\d\d\d-\d\d\d\d-\d\d\d\d)'
{
Write-Warning "message contains a credit card number: $($matches.CC)"
}
'(?<Phone>\d\d\d-\d\d\d-\d\d\d\d)'
{
Write-Warning "message contains a phone number: $($matches.Phone)"
}
}
WARNING: message may contain a SSN: 123-23-3456
WARNING: message may contain a credit card number: 1234-5678-1234-5678
$null
Sie können einen $null
-Wert abgleichen, der nicht der Standardwert sein muss.
$values = '', 5, $null
switch ( $values )
{
$null { "Value '$_' is `$null" }
{ '' -eq $_ } { "Value '$_' is an empty string" }
default { "Value [$_] isn't an empty string or `$null" }
}
Value '' is an empty string
Value [5] isn't an empty string or $null
Value '' is $null
Wenn Sie auf eine leere Zeichenfolge in einer switch
-Anweisung prüfen, ist es wichtig, die Vergleichsanweisung wie in diesem Beispiel zu verwenden, anstelle des unformatierten Werts ''
. In einer switch
-Anweisung entspricht der unformatierte ''
-Wert auch $null
. Beispiel:
$values = '', 5, $null
switch ( $values )
{
$null { "Value '$_' is `$null" }
'' { "Value '$_' is an empty string" }
default { "Value [$_] isn't an empty string or `$null" }
}
Value '' is an empty string
Value [5] isn't an empty string or $null
Value '' is $null
Value '' is an empty string
Seien Sie außerdem vorsichtig mit leeren Rückgaben von Cmdlets. Cmdlets oder Pipelines, die keine Ausgabe aufweisen, werden als leeres Array behandelt, das keine Übereinstimmung hat, einschließlich des default
-Falls.
$file = Get-ChildItem NonExistantFile*
switch ( $file )
{
$null { '$file is $null' }
default { "`$file is type $($file.GetType().Name)" }
}
# No matches
Konstanter Ausdruck
Lee Dailey hat darauf hingewiesen, dass wir einen Ausdruck, der konstant $true
ist, nutzen können, um [bool]
-Elemente auszuwerten.
Stellen Sie sich vor, wir müssen mehrere Prüfungen boolescher Werte durchführen.
$isVisible = $false
$isEnabled = $true
$isSecure = $true
switch ( $true )
{
$isEnabled
{
'Do-Action'
}
$isVisible
{
'Show-Animation'
}
$isSecure
{
'Enable-AdminMenu'
}
}
Do-Action
Enabled-AdminMenu
Dies ist eine einfache Möglichkeit, den Status mehrerer boolescher Felder auszuwerten und entsprechende Maßnahmen zu ergreifen. Das Besondere daran ist, dass Sie mit einer Übereinstimmung den Status eines noch nicht ausgewerteten Werts umdrehen können.
$isVisible = $false
$isEnabled = $true
$isAdmin = $false
switch ( $true )
{
$isEnabled
{
'Do-Action'
$isVisible = $true
}
$isVisible
{
'Show-Animation'
}
$isAdmin
{
'Enable-AdminMenu'
}
}
Do-Action
Show-Animation
Das Festlegen von $isEnabled
auf $true
in diesem Beispiel stellt sicher, dass $isVisible
auch auf $true
festgelegt wird.
Wenn $isVisible
dann ausgewertet wird, wird sein Skriptblock aufgerufen. Das ist ein wenig widersinnig, aber eine geschickte Nutzung vorhandener Mechanismen.
$switch (automatische Variable)
Wenn switch
seine Werte verarbeitet, wird ein Enumerator erstellt, der $switch
genannt wird. Dies ist eine von PowerShell automatisch erstellte Variable, die Sie direkt verändern können.
$a = 1, 2, 3, 4
switch($a) {
1 { [void]$switch.MoveNext(); $switch.Current }
3 { [void]$switch.MoveNext(); $switch.Current }
}
Dadurch erhalten Sie die folgenden Ergebnisse:
2
4
Indem Sie den Enumerator vorwärts bewegen, wird das nächste Element nicht durch switch
verarbeitet, aber Sie können direkt auf diesen Wert zugreifen. Das würde ich Aberwitz nennen.
Andere Muster
Hashtabellen
Einer meiner beliebtesten Beiträge ist der zu Hashtabellen. Einer der Anwendungsfälle für eine hashtable
soll eine Nachschlagetabelle sein. Das ist eine alternative Annäherung an ein gängiges Muster, mit dem sich eine switch
-Anweisung oft befasst.
$day = 3
$lookup = @{
0 = 'Sunday'
1 = 'Monday'
2 = 'Tuesday'
3 = 'Wednesday'
4 = 'Thursday'
5 = 'Friday'
6 = 'Saturday'
}
$lookup[$day]
Wednesday
Wenn ich eine switch
-Anweisung nur zum Nachschlagen verwende, nutze ich stattdessen oft eine hashtable
.
Enum
Seit PowerShell 5.0 gibt es Enum
, was in diesem Fall auch eine Option ist.
$day = 3
enum DayOfTheWeek {
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
}
[DayOfTheWeek]$day
Wednesday
Wir könnten den ganzen Tag damit verbringen, verschiedene Wege zur Lösung dieses Problems zu finden. Ich wollte nur sichergehen, dass Sie wissen, welche Möglichkeiten Sie haben.
Schlusswort
Die switch-Anweisung ist oberflächlich betrachtet einfach, aber sie bietet einige erweiterte Merkmale, von denen die meisten nicht wissen, dass sie zur Verfügung stehen. Durch die Kombination dieser Merkmale steht Ihnen ein leistungsstarkes Instrument zur Verfügung. Ich hoffe, Sie haben etwas gelernt, was Sie vorher noch nicht wussten.