Wszystko, co kiedykolwiek chciałeś wiedzieć o instrukcji switch

Podobnie jak w przypadku wielu innych języków program PowerShell ma polecenia służące do kontrolowania przepływu wykonywania w skryptach. Jedną z tych instrukcji jest instrukcja switch i w programie PowerShell oferuje funkcje, które nie znajdują się w innych językach. Obecnie szczegółowo omówimy pracę z programem PowerShell switch.

Uwaga

Oryginalna wersja tego artykułu pojawiła się na blogu napisanym przez @KevinMarquette. Zespół programu PowerShell dziękuje Kevinowi za udostępnienie tej zawartości nam. Zapoznaj się ze swoim blogiem na PowerShellExplained.com.

Instrukcja if

Jedną z pierwszych instrukcji, które poznasz, jest if instrukcja . Umożliwia wykonanie bloku skryptu, jeśli instrukcja to $true.

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

Można mieć znacznie bardziej skomplikowaną logikę przy użyciu instrukcji elseif i .else Oto przykład, w którym mam wartość liczbową dla dnia tygodnia i chcę uzyskać nazwę jako ciąg.

$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

Okazuje się, że jest to typowy wzorzec i istnieje wiele sposobów radzenia sobie z tym. Jeden z nich ma wartość switch.

Switch, instrukcja

Instrukcja switch umożliwia podanie zmiennej i listy możliwych wartości. Jeśli wartość jest zgodna ze zmienną, zostanie wykonany jej skryptblock.

$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'

W tym przykładzie wartość $day jest zgodna z jedną z wartości liczbowych, a następnie jest przypisana prawidłowa nazwa do $result. W tym przykładzie wykonujemy tylko przypisanie zmiennych, ale w tych blokach skryptu można wykonać dowolny program PowerShell.

Przypisywanie do zmiennej

Możemy napisać ten ostatni przykład w inny sposób.

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

Umieszczamy wartość w potoku programu PowerShell i przypisujemy ją do elementu $result. Możesz to zrobić za pomocą instrukcji if i foreach .

Wartość domyślna

Możemy użyć słowa kluczowego default , aby zidentyfikować, co powinno się zdarzyć, jeśli nie ma dopasowania.

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

W tym miejscu zwracamy wartość Unknown w przypadku domyślnym.

Ciągi

W tych ostatnich przykładach pasowałem do liczb, ale można również dopasować ciągi.

$item = 'Role'

switch ( $item )
{
    Component
    {
        'is a component'
    }
    Role
    {
        'is a role'
    }
    Location
    {
        'is a location'
    }
}
is a role

Postanowiłem nie opakowować Componentelementu iRoleLocation dopasować w cudzysłowie tutaj, aby podkreślić, że są opcjonalne. W większości przypadków element switch traktuje te jako ciąg.

Tablice

Jedną z chłodnych funkcji programu PowerShell switch jest sposób obsługi tablic. Jeśli nadasz tablicy switch , przetwarza każdy element w tej kolekcji.

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

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

Jeśli elementy tablicy są powtarzane, są one dopasowywane wiele razy przez odpowiednią sekcję.

PsItem

Możesz użyć elementu $PSItem lub $_ , aby odwołać się do bieżącego przetworzonego elementu. Gdy wykonamy proste dopasowanie, $PSItem to wartość, którą dopasowujemy. Będę wykonywać kilka zaawansowanych dopasowań w następnej sekcji, w której jest używana ta zmienna.

Parametry

Unikatową funkcją programu PowerShell switch jest to, że ma ona wiele parametrów przełącznika, które zmieniają sposób jego działania.

-Casesensitive

Dopasowania nie są domyślnie uwzględniane w wielkości liter. Jeśli musisz mieć wielkość liter, możesz użyć polecenia -CaseSensitive. Może to być używane w połączeniu z innymi parametrami przełącznika.

-Symbol wieloznaczny

Możemy włączyć obsługę symboli wieloznacznych za pomocą przełącznika -wildcard . Używa to tej samej logiki wieloznacznych co -like operator do wykonania każdego dopasowania.

$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

W tym miejscu przetwarzamy komunikat, a następnie generujemy go na różnych strumieniach na podstawie zawartości.

-Regex

Instrukcja switch obsługuje wyrażenia regularne, podobnie jak w przypadku symboli wieloznacznych.

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

Mam więcej przykładów używania wyrażenia regularnego w innym artykule napisałem: Wiele sposobów używania wyrażenia regularnego.

-Plik

Mało znaną funkcją instrukcji switch jest to, że może przetworzyć plik za pomocą parametru -File . Używasz -file ścieżki do pliku zamiast nadawać mu wyrażenie zmiennej.

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

Działa tak samo jak przetwarzanie tablicy. W tym przykładzie połączę go z dopasowaniem symboli wieloznacznych i użyjemy elementu $PSItem. Spowoduje to przetworzenie pliku dziennika i przekonwertowanie go na komunikaty ostrzegawcze i komunikaty o błędach w zależności od dopasowań wyrażeń regularnych.

Szczegóły zaawansowane

Teraz, gdy znasz wszystkie te udokumentowane funkcje, możemy ich używać w kontekście bardziej zaawansowanego przetwarzania.

Wyrażenia

Element switch może znajdować się w wyrażeniu zamiast zmiennej.

switch ( ( Get-Service | Where status -eq 'running' ).name ) {...}

Niezależnie od tego, jakie wyrażenie daje w ocenie, jest wartość użyta dla dopasowania.

Wiele dopasowań

Być może została już odebrana, ale może być zgodna z switch wieloma warunkami. Jest to szczególnie prawdziwe w przypadku używania -wildcard lub -regex dopasowania. Ten sam warunek można dodać wiele razy i wszystkie są wyzwalane.

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

Wszystkie trzy z tych instrukcji są wyzwalane. Pokazuje to, że każdy warunek jest sprawdzany (w kolejności). Dotyczy to przetwarzania tablic, w których każdy element sprawdza każdy warunek.

Kontynuuj

Zwykle jest to miejsce, w którym wprowadzałbym break oświadczenie, ale lepiej jest nauczyć się używać continue najpierw. Podobnie jak w przypadku foreach pętli, continue kontynuuje na następnym elemencie w kolekcji lub kończy switch działanie, jeśli nie ma więcej elementów. Możemy ponownie napisać ten ostatni przykład z instrukcjami continue, aby wykonać tylko jedną instrukcję.

switch ( 'Word' )
{
    'word'
    {
        'lower case word match'
        continue
    }
    'Word'
    {
        'mixed case word match'
        continue
    }
    'WORD'
    {
        'upper case word match'
        continue
    }
}
lower case word match

Zamiast dopasowywać wszystkie trzy elementy, pierwszy z nich jest zgodny, a przełącznik kontynuuje następną wartość. Ponieważ nie ma wartości do przetworzenia, przełącznik kończy działanie. W następnym przykładzie pokazano, jak symbol wieloznaczny może być zgodny z wieloma elementami.

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

Ponieważ wiersz w pliku wejściowym może zawierać zarówno słowo Error , jak i Warning, chcemy tylko, aby pierwszy wiersz został wykonany, a następnie kontynuować przetwarzanie pliku.

Przerwa

Instrukcja break zamyka przełącznik. Jest to to samo zachowanie, które continue przedstawia pojedyncze wartości. Różnica jest wyświetlana podczas przetwarzania tablicy. break zatrzymuje wszystkie operacje przetwarzania w przełączniku i continue przechodzi do następnego elementu.

$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

W takim przypadku, jeśli trafimy na jakiekolwiek wiersze rozpoczynające się od Error , zostanie wyświetlony błąd i przełącznik zostanie zatrzymany. To właśnie break to stwierdzenie robi dla nas. Jeśli znajdziemy Error wewnątrz ciągu, a nie tylko na początku, napiszemy go jako ostrzeżenie. Robimy to samo dla Warning. Możliwe, że wiersz może zawierać zarówno słowo Error , jak i Warning, ale potrzebujemy tylko jednego do przetworzenia. To jest to, co continue robi dla nas oświadczenie.

Podział etykiet

Instrukcja switch obsługuje break/continue etykiety tak samo jak 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
        }
    }
}

Osobiście nie lubię używania etykiet break, ale chciałem je wskazać, ponieważ są one mylące, jeśli nigdy wcześniej ich nie widziałeś. Jeśli masz wiele switch instrukcji lub foreach , które są zagnieżdżone, możesz chcieć wydzielić więcej niż wewnętrzny najbardziej element. Możesz umieścić etykietę na obiekcie switch , która może być elementem docelowym obiektu break.

Wyliczenie

Program PowerShell 5.0 dał nam wyliczenia i możemy użyć ich w przełączniku.

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

Jeśli chcesz zachować wszystko tak mocno typizowane wyliczenia, możesz umieścić je w nawiasach.

switch ($item )
{
    ([Context]::Component)
    {
        'is a component'
    }
    ([Context]::Role)
    {
        'is a role'
    }
    ([Context]::Location)
    {
        'is a location'
    }
}

Nawiasy są potrzebne w tym miejscu, aby przełącznik nie traktować wartości [Context]::Location jako ciągu literału.

ScriptBlock

W razie potrzeby możemy użyć blokady skryptu, aby przeprowadzić ocenę dopasowania.

$age = 37

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

To zwiększa złożoność i może utrudnić switch czytanie. W większości przypadków, w których należy użyć czegoś takiego, lepiej byłoby użyć if instrukcji i elseif . Rozważę użycie tego, gdybym miał już duży przełącznik i potrzebowałem dwóch elementów, aby trafić w ten sam blok oceny.

Jedną z rzeczy, które myślę, że pomaga w czytelności jest umieszczenie scriptblock w nawiasach.

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

Nadal wykonuje to samo działanie i daje lepszą przerwę wizualną podczas szybkiego przeglądania.

$matches wyrażeń regularnych

Musimy ponownie zwrócić się do regex, aby dotknąć czegoś, co nie jest natychmiast oczywiste. Użycie wyrażenia regularnego wypełnia zmienną $matches . Idę do użycia $matches więcej, kiedy mówię o wielu sposobach korzystania z regex. Oto szybki przykład pokazujący go w akcji z nazwanymi dopasowaniami.

$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

Możesz dopasować $null wartość, która nie musi być wartością domyślną.

$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

Podczas testowania pustego ciągu w switch instrukcji ważne jest użycie instrukcji porównania, jak pokazano w tym przykładzie zamiast wartości ''pierwotnej . W instrukcji switch wartość '' nieprzetworzona jest również zgodna $nullz wartością . Na przykład:

$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

Ponadto należy zachować ostrożność przy użyciu pustych zwrotów z poleceń cmdlet. Polecenia cmdlet lub potoki, które nie mają danych wyjściowych, są traktowane jako pusta tablica, która nie pasuje do niczego, w tym przypadku default .

$file = Get-ChildItem NonExistantFile*
switch ( $file )
{
    $null   { '$file is $null' }
    default { "`$file is type $($file.GetType().Name)" }
}
# No matches

Wyrażenie stałe

Lee Dailey zwrócił uwagę, że możemy użyć wyrażenia stałego $true do oceny [bool] elementów. Wyobraź sobie, że mamy kilka testów logicznych, które muszą się zdarzyć.

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

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

Jest to czysty sposób oceniania i podejmowania działań na temat stanu kilku pól logicznych. Fajną rzeczą w tym jest to, że można mieć jeden mecz przerzucić stan wartości, która nie została jeszcze obliczona.

$isVisible = $false
$isEnabled = $true
$isAdmin = $false

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

Ustawienie $isEnabled na $true wartość w tym przykładzie gwarantuje również, że $isVisible ustawiono $truewartość . Następnie po $isVisible obliczeniu zostanie wywołany jego blok skryptu. Jest to nieco odwrotnie intuicyjne, ale jest sprytnym zastosowaniem mechaniki.

$switch zmienną automatyczną

Gdy obiekt switch przetwarza jego wartości, tworzy moduł wyliczający i wywołuje go $switch. Jest to zmienna automatyczna utworzona przez program PowerShell i można nią manipulować bezpośrednio.

$a = 1, 2, 3, 4

switch($a) {
    1 { [void]$switch.MoveNext(); $switch.Current }
    3 { [void]$switch.MoveNext(); $switch.Current }
}

Daje to wyniki:

2
4

Przenosząc moduł wyliczający do przodu, następny element nie jest przetwarzany przez switch element , ale możesz uzyskać bezpośredni dostęp do tej wartości. Nazwałbym to szaleństwem.

Inne wzorce

Tabele skrótów

Jednym z moich najpopularniejszych postów jest ten, który zrobiłem na tabelach skrótów. Jednym z przypadków użycia elementu hashtable jest tabela odnośników. Jest to alternatywne podejście do wspólnego wzorca, którego switch często dotyczy instrukcja.

$day = 3

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

$lookup[$day]
Wednesday

Jeśli używam switch tylko elementu jako odnośnika, często używam elementu hashtable .

Wyliczenie

Program PowerShell 5.0 wprowadził Enum funkcję i jest również opcją w tym przypadku.

$day = 3

enum DayOfTheWeek {
    Sunday
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
}

[DayOfTheWeek]$day
Wednesday

Moglibyśmy przejść przez cały dzień, patrząc na różne sposoby rozwiązania tego problemu. Chciałem tylko upewnić się, że wiesz, że masz opcje.

Końcowe wyrazy

Instrukcja switch jest prosta na powierzchni, ale oferuje niektóre zaawansowane funkcje, które większość ludzi nie zdaje sobie sprawy, są dostępne. Łączenie tych funkcji sprawia, że jest to zaawansowana funkcja. Mam nadzieję, że nauczyłeś się czegoś, czego wcześniej nie zdałeś sobie sprawy.