Freigeben über


Alles, was Sie jemals über die Switch-Anweisung wissen wollten

Wie viele andere Sprachen verfügt PowerShell über Befehle zum Steuern des Ausführungsflusses in Ihren Skripts. Eine dieser Anweisungen ist die Switch-Anweisung, und in PowerShell bietet sie Funktionen, die in anderen Sprachen nicht zu finden sind. Heute nehmen wir einen tiefen Einblick in die Arbeit mit der PowerShell switch.

Hinweis

Die originale Version dieses Artikels erschien auf dem Blog, der von @KevinMarquette geschrieben wurde. Das PowerShell-Team danke Kevin für das Teilen dieser Inhalte mit uns. Bitte schauen Sie sich seinen Blog bei PowerShellExplained.com an.

Die Anweisung if

Eine der ersten Anweisungen, die Sie erlernen, ist die if Anweisung. Sie können einen Skriptblock ausführen, wenn eine Anweisung lautet $true.

if ( Test-Path $Path )
{
    Remove-Item $Path
}

Sie können viel komplexere Logik mit Anweisungen elseif und else verwenden. Hier ist ein Beispiel, in dem ich einen numerischen Wert für den Wochentag habe und 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 gängiges Muster ist und es viele Möglichkeiten gibt, dies zu bewältigen. Einer davon ist mit einem switch.

Switch-Anweisung

Mit der switch Anweisung können Sie eine Variable und eine Liste möglicher Werte angeben. Wenn der Wert der Variablen entspricht, wird der Anweisungsblock 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, und der richtige Name wird $result zugewiesen. In diesem Beispiel wird nur eine Variable zugewiesen, aber jede PowerShell kann in diesen Skriptblöcken ausgeführt werden.

Zuweisen zu einer Variablen

Wir können dieses letzte Beispiel auf eine andere Weise schreiben.

$result = switch ( $day )
{
    0 { 'Sunday'    }
    1 { 'Monday'    }
    2 { 'Tuesday'   }
    3 { 'Wednesday' }
    4 { 'Thursday'  }
    5 { 'Friday'    }
    6 { 'Saturday'  }
}

Wir setzen den Wert in die PowerShell-Pipeline und weisen ihn in die Variable $result zu. Sie können dasselbe mit den Anweisungen if und foreach tun.

Standard

Wir können das default Schlüsselwort verwenden, um zu identifizieren, was passieren soll, wenn keine Übereinstimmung vorhanden ist.

$result = switch ( $day )
{
    0 { 'Sunday' }
    # ...
    6 { 'Saturday' }
    default { 'Unknown' }
}

Hier wird der Wert Unknown im Standardfall zurückgegeben.

Streichinstrumente

Ich habe in den letzten Beispielen Zahlen 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 Treffer Component, Role und Location hier nicht in Anführungszeichen zu setzen, um hervorzuheben, dass sie optional sind. In den meisten Fällen behandelt switch diese als Zeichenfolge.

Felder

Eine der coolen Funktionen der PowerShell switch ist, wie sie mit Arrays umgeht. Wenn Sie ein switch Array angeben, verarbeitet es jedes Element in dieser Sammlung.

$roles = @('WEB','Database')

switch ( $roles ) {
    'Database'   { 'Configure SQL' }
    'WEB'        { 'Configure IIS' }
    'FileServer' { 'Configure Share' }
}
Configure IIS
Configure SQL

Wenn Sie elemente in Ihrem Array wiederholt haben, werden sie mehrmals vom entsprechenden Abschnitt abgeglichen.

PSItem

Sie können das aktuelle Element $PSItem verwenden oder auf das verarbeitete $_ Element verweisen. Wenn wir eine einfache Übereinstimmung durchführen, ist $PSItem der Wert, den wir abgleichen. Ich werde im nächsten Abschnitt, in dem diese Variable verwendet wird, einige fortgeschrittene Abgleiche durchführen.

Die Parameter

Ein einzigartiges Feature der PowerShell switch besteht darin, dass es über eine Reihe von Switchparametern verfügt, die die Leistung ändern.

-Casesensitive

Bei den Übereinstimmungen wird standardmäßig keine Groß-/Kleinschreibung beachtet. Wenn die Groß-/Kleinschreibung beachtet werden muss, können Sie -CaseSensitive verwenden. Dies kann in Kombination mit den anderen Schalterparametern verwendet werden.

-Wildcard

Wir können die Unterstützung von Wildcards mit dem -Wildcard Schalter aktivieren. Dies verwendet dieselbe Wildcard-Logik wie dem -like-Operator, um jede Übereinstimmung durchzuführen.

$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 basierend auf dem Inhalt auf verschiedenen Datenströmen aus.

-Regex

Die Switch-Anweisung unterstützt regex-Übereinstimmungen genau wie Wildcards.

switch -Regex ( $message )
{
    '^Error'
    {
        Write-Error -Message $Message
    }
    '^Warning'
    {
        Write-Warning -Message $Message
    }
    default
    {
        Write-Information $message
    }
}

Ich habe weitere Beispiele für die Verwendung von regex in einem anderen Artikel, den ich geschrieben habe: Die vielen Möglichkeiten, regex zu verwenden.

-File

Ein wenig bekanntes Feature der Switch-Anweisung besteht darin, dass sie eine Datei mit dem -File Parameter verarbeiten kann. Sie verwenden -File mit einem Pfad zu einer Datei, anstatt einen Variablenausdruck zu geben.

switch -Wildcard -File $path
{
    'Error*'
    {
        Write-Error -Message $PSItem
    }
    'Warning*'
    {
        Write-Warning -Message $PSItem
    }
    default
    {
        Write-Output $PSItem
    }
}

Es funktioniert genauso wie die Verarbeitung eines Arrays. In diesem Beispiel kombiniere ich es mit Wildcard-Matching und verwende das $PSItem. Dadurch würde eine Protokolldatei verarbeitet und in Warnungs- und Fehlermeldungen konvertiert, je nach regex-Übereinstimmungen.

Erweiterte Details

Da Sie sich nun mit all diesen dokumentierten Funktionen vertraut gemacht haben, können wir sie im Kontext einer fortgeschritteneren Verarbeitung verwenden.

Ausdrücke

Das switch kann sich auf einen Ausdruck anstelle einer Variablen beziehen.

switch ( ( Get-Service | where Status -EQ 'running' ).Name ) {...}

Was auch immer der Ausdruck ergibt, ist der Wert, der für den Match verwendet wird.

Mehrere Übereinstimmungen

Möglicherweise haben Sie dies bereits bemerkt, aber eine switch kann mehreren Bedingungen entsprechen. Dies gilt insbesondere bei der Verwendung von -Wildcard oder -Regex Übereinstimmungen. 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 überprüft wird (in der Reihenfolge). Dies gilt für die Verarbeitung von Arrays, bei denen jedes Element jede Bedingung überprüft.

Weiter

Normalerweise würde ich die break Anweisung vorstellen, aber es ist besser, dass wir lernen, wie man zuerst continue verwendet. Ähnlich wie bei einer foreach Schleife geht es mit dem nächsten Element in der Auflistung weiter oder verlässt die switch, wenn keine weiteren Elemente vorhanden sind. Wir können das letzte Beispiel mit continue-Anweisungen neu schreiben, sodass 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 Element abgeglichen, dann wird mit dem nächsten Wert fortgesetzt. Da keine Zu verarbeitenden Werte vorhanden sind, wird der Schalter beendet. Im nächsten Beispiel wird gezeigt, wie ein Wildcard 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 Warning enthalten kann, möchten wir, dass nur das erste ausgeführt wird und die Verarbeitung der Datei dann fortgesetzt wird.

Brechen

Eine break Anweisung beendet den Schalter. Dies ist dasselbe Verhalten, das continue für einzelne Werte darstellt. Der Unterschied wird beim Verarbeiten eines Arrays angezeigt. break beendet die gesamte Verarbeitung im Schalter und continue wechselt zum nächsten Element.

$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

In diesem Fall, wenn wir auf Linien treffen, die mit Error beginnen, wird ein Fehler angezeigt, und der Schalter stoppt. Das ist, was diese break Aussage für uns tut. Wenn wir Error innerhalb der Zeichenfolge und nicht nur am Anfang finden, schreiben wir es als Warnung. Wir tun dasselbe für Warning. Es ist möglich, dass eine Zeile sowohl das Wort Error als auch das Wort Warning haben könnte, aber wir müssen nur eines verarbeiten. Das ist das, was die continue Aussage für uns tut.

Trennbeschriftungen

Die switch Anweisung unterstützt break/continue Labels 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
        }
    }
}

Ich persönlich mag die Verwendung von Break-Labels nicht, doch ich wollte darauf hinweisen, da sie verwirrend sein können, wenn man sie noch nie gesehen hat. Wenn Sie mehrere switch oder foreach Anweisungen haben, die geschachtelt sind, möchten Sie möglicherweise aus mehr als nur der innersten Ebene ausbrechen. Sie können ein Etikett auf einem switch platzieren, das Ziel Ihrer break sein kann.

Enum

PowerShell 5.0 führte Enums ein, und wir können sie in einem Switch verwenden.

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 Enums 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 der Wert [Context]::Location vom Switch nicht als Literalzeichenfolge behandelt wird.

Skriptblock

Wir können bei Bedarf einen Scriptblock verwenden, um die Überprüfung für eine Übereinstimmung durchzuführen.

$age = 37

switch ( $age )
{
    {$PSItem -le 18}
    {
        'child'
    }
    {$PSItem -gt 18}
    {
        'adult'
    }
}
'adult'

Dies erhöht die Komplexität und kann Ihr switch schwer lesbar machen. In den meisten Fällen, in denen Sie so etwas verwenden würden, wäre es besser, if und elseif Anweisungen zu nutzen. Ich würde dies in Betracht ziehen, wenn ich bereits über einen großen Schalter verfügte und ich zwei Elemente benötigt habe, um denselben Auswertungsblock zu treffen.

Eine Sache, die mir bei der Lesbarkeit hilft, ist das Platzieren des Scriptblocks in Klammern.

switch ( $age )
{
    ({$PSItem -le 18})
    {
        'child'
    }
    ({$PSItem -gt 18})
    {
        'adult'
    }
}

Sie wird immer noch auf die gleiche Weise ausgeführt und bietet beim schnellen Betrachten einen besseren visuellen Bruch.

Regex Matches

Wir müssen regex erneut betrachten, um auf etwas einzugehen, das nicht sofort erkennbar ist. Die Verwendung von "regex" füllt die $Matches Variable auf. Ich gehe in die Verwendung von $Matches mehr, wenn ich über die vielen Möglichkeiten zur Verwendung von regex spreche. Hier ist ein kurzes Beispiel, um es in Aktion mit benannten Matches 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 verwenden, der nicht Standard 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

Beim Testen einer leeren Zeichenfolge in einer switch Anweisung ist es wichtig, die Vergleichsanweisung wie in diesem Beispiel anstelle des Rohwerts ''zu verwenden. In einer switch-Anweisung stimmt der Rohwert '' ebenfalls mit $null überein. 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

Achten Sie außerdem bei leeren Rückgaben von Cmdlets darauf. Cmdlets oder Pipelines, die keine Ausgabe haben, werden als leeres Array behandelt, das keine Übereinstimmung mit etwas 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 konstanten $true Ausdruck verwenden können, um Elemente auszuwerten [bool] . Stellen Sie sich vor, ob wir mehrere boolesche Überprüfungen haben, die passieren müssen.

$isVisible = $false
$isEnabled = $true
$isSecure = $true

switch ( $true )
{
    $isEnabled
    {
        'Do-Action'
    }
    $isVisible
    {
        'Show-Animation'
    }
    $isSecure
    {
        'Enable-AdminMenu'
    }
}
Do-Action
Enabled-AdminMenu

Dies ist eine saubere Methode, den Status mehrerer boolescher Felder zu bewerten und entsprechend zu handeln. Das Tolle daran ist, dass Sie mit einer Übereinstimmung den Status eines noch nicht ausgewerteten Werts ändern 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

Die Einstellung von $isEnabled auf $true in diesem Beispiel stellt sicher, dass $isVisible auch auf $true gesetzt wird. Wenn $isVisible ausgewertet wird, wird dessen Anweisungsblock aufgerufen. Dies ist etwas kontraintuitiv, aber eine clevere Verwendung der Mechanik.

$switch automatische Variable

Wenn switch seine Werte verarbeitet, erstellt es einen Enumerator und ruft ihn $switch auf. Dies ist eine von PowerShell erstellte automatische Variable, die Sie direkt bearbeiten 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 Ergebnisse von:

2
4

Indem Sie den Enumerator vorwärts verschieben, wird das nächste Element nicht von dem switch verarbeitet, Sie können aber direkt auf diesen Wert zugreifen. Ich würde es Verrücktheit nennen.

Andere Muster

Hashtabellen

Einer meiner beliebtesten Beiträge ist die, die ich auf Hashtables gemacht habe. Einer der Anwendungsfälle für eine hashtable ist eine Nachschlagetabelle. Dies ist ein alternativer Ansatz für ein gängiges Muster, auf das häufig eine switch-Anweisung anspricht.

$day = 3

$lookup = @{
    0 = 'Sunday'
    1 = 'Monday'
    2 = 'Tuesday'
    3 = 'Wednesday'
    4 = 'Thursday'
    5 = 'Friday'
    6 = 'Saturday'
}

$lookup[$day]
Wednesday

Wenn ich switch nur zum Nachschlagen nutze, verwende ich stattdessen häufig hashtable.

Enum

PowerShell 5.0 hat dies enum eingeführt und ist auch in diesem Fall eine Option.

$day = 3

enum DayOfTheWeek {
    Sunday
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
}

[DayOfTheWeek]$day
Wednesday

Wir könnten den ganzen Tag damit verbringen, verschiedene Ansätze zu betrachten, um dieses Problem zu lösen. Ich wollte nur sicherstellen, dass Sie wussten, dass Sie Optionen hatten.

Endgültige Wörter

Die Switch-Anweisung ist auf den ersten Blick einfach, bietet aber einige erweiterte Funktionen, die den meisten Personen nicht bewusst sind. Durch die Kombination dieser Merkmale entsteht ein leistungsstarkes Feature. Ich hoffe, Sie haben etwas gelernt, das Sie vorher nicht erkannt hatten.