Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
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 switch instrukcji 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 prowadzonym 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 instrukcja if
. Umożliwia wykonanie bloku skryptu, jeśli twierdzenie jest $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. Jednym z nich jest "switch
".
Instrukcja switch
Instrukcja switch
umożliwia podanie zmiennej i listy możliwych wartości. Jeśli wartość odpowiada zmiennej, wówczas zostanie wykonany jej blok skryptowy.
$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 prawidłowa nazwa jest przypisywana do $result
. W tym przykładzie wykonujemy tylko przypisanie zmiennych, ale w tych blokach skryptu można wykonać dowolny program PowerShell.
Przypisać 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 $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 dopasowywałem liczby, a 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 ujmować w cudzysłów dopasowań Component
,Role
i Location
tutaj, aby podkreślić, że są opcjonalne. W większości przypadków switch
traktuje to jako ciąg.
Tablice
Jedną z interesujących funkcji PowerShell switch
jest sposób obsługi tablic. Jeśli przekażesz switch
tablicę, przetworzy 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ć $PSItem
lub $_
, aby odwołać się do bieżącego przetworzonego elementu. Gdy wykonujemy proste dopasowanie, $PSItem
jest wartością, którą porównujemy. Będę wykonywać kilka zaawansowanych dopasowań w następnej sekcji, w której jest używana ta zmienna.
Parametry
Unikatową funkcją switch
programu PowerShell 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 uwzględniać wielkość liter, możesz użyć -CaseSensitive
. Może to być używane w połączeniu z innymi parametrami przełącznika.
-Znak wieloznaczny
Możemy włączyć obsługę symboli wieloznacznych za pomocą przełącznika -Wildcard
. To wykorzystuje tę samą logikę wieloznaczników co operator -like
do 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 kanałach w zależności od jego zawartości.
-Regex
Instrukcja switch obsługuje wyrażenia regularne, tak samo jak symbole wieloznaczne.
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żeń regularnych w innym artykule, który napisałem: Wiele sposobów użycia wyrażeń regularnych.
-Plik
Mało znaną funkcją instrukcji switch jest to, że może przetworzyć plik za pomocą parametru -File
. Należy użyć -File
ze ścieżką do pliku zamiast nadać 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 łączę go z dopasowaniem wieloznacznym i używam $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
switch
może znajdować się w wyrażeniu zamiast zmiennej.
switch ( ( Get-Service | where Status -EQ 'running' ).Name ) {...}
Każda wartość, do której zostanie wyrażenie wyliczone, jest wartością używaną do dopasowania.
Wiele dopasowań
Być może już to zauważyłeś, ale switch
może pasować do wielu warunków. Jest to szczególnie istotne w przypadku używania dopasowań -Wildcard
lub -Regex
. 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 wprowadziłbym oświadczenie break
, ale lepiej jest nauczyć się używać continue
najpierw. Podobnie jak w przypadku pętli foreach
, continue
przechodzi do następnego elementu w kolekcji lub opuszcza switch
, 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 dopasowywany, a przełącznik przechodzi do następnej wartości. Ponieważ nie ma wartości do przetworzenia, przełącznik kończy działanie. W następnym przykładzie pokazano, jak symbol wieloznaczny może dopasować się do wielu elementów.
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 wyraz Error
, jak i Warning
, chcemy wykonać tylko pierwszy wiersz, a następnie kontynuować przetwarzanie pliku.
Przerwa
Instrukcja break
kończy przełączanie. Jest to takie samo zachowanie, które continue
przedstawia dla pojedynczych wartości. Różnica jest wyświetlana podczas przetwarzania tablicy.
break
zatrzymuje wszystkie operacje przetwarzania w przełączniku i continue
przechodzi na następny 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
W takim przypadku, jeśli trafimy na wiersze rozpoczynające się od Error
zostanie wyświetlony błąd i przełącznik zostanie zatrzymany.
To jest to, co to break
oświadczenie robi dla nas. Jeśli znajdziemy Error
wewnątrz ciągu, a nie tylko na początku, zapisujemy go jako ostrzeżenie. Robimy to samo dla Warning
. Możliwe, że linia może zawierać zarówno słowo Error
, jak i Warning
, ale potrzebujemy tylko jednego do przetworzenia. To jest to, co robi dla nas deklaracja continue
.
Podział etykiet
Instrukcja switch
obsługuje etykiety break/continue
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 instrukcji switch
lub foreach
, które są zagnieżdżone, możesz chcieć przerwać więcej niż tylko wewnętrzny element. Etykietę można umieścić na switch
, która może być celem dla Twojego 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 jako 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ą tutaj potrzebne, aby przełącznik nie traktował wartości [Context]::Location
jako literału tekstowego.
ScriptBlock
W razie potrzeby możemy użyć bloku 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 sprawić, że switch
staje się trudne do odczytania. W większości przypadków, w których należy użyć czegoś takiego, lepiej byłoby użyć instrukcji if
i elseif
. Rozważyłbym użycie tego, jeśli miałbym już duży przełącznik i potrzebowałbym dwóch elementów, aby trafiły do tego samego bloku oceny.
Jedną z rzeczy, która moim zdaniem poprawia czytelność, 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.
Dopasowania wyrażeń regularnych $Matches
Musimy ponownie zwrócić się do regex, aby dotknąć czegoś, co nie jest natychmiast oczywiste. Użycie wyrażenia regularnego wypełnia zmienną $Matches
. Zajmuję się użyciem $Matches
bardziej szczegółowo, gdy mówię o wielu sposobach użycia regex. Oto szybki przykład ilustrujący jego działanie z nazwanymi grupami dopasowań.
$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ć wartość $null
, 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 instrukcji switch
należy użyć instrukcji porównania, jak pokazano w tym przykładzie zamiast wartości pierwotnej ''
. W instrukcji switch
wartość nieprzetworzona ''
jest również zgodna z $null
. 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 wyników z cmdlet. Polecenia cmdlet lub potoki bez danych wyjściowych są traktowane jako pusta tablica, która nie pasuje do niczego, łącznie z przypadkiem 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ć stałego wyrażenia $true
do oceny elementów [bool]
.
Wyobraź sobie, że mamy kilka sprawdzeń logicznych, które muszą zostać przeprowadzone.
$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 jedno porównanie może zmienić stan wartości, która nie została jeszcze oceniona.
$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
w tym przykładzie gwarantuje, że $isVisible
jest również ustawiona na wartość $true
.
Następnie, gdy $isVisible
zostanie oceniony, zostanie wywołany jego blok skryptu. Jest to nieco nieintuicyjne, ale stanowi sprytne zastosowanie mechaniki.
$Switch zmienna automatyczna
Gdy switch
przetwarza swoje 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
, 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 hashtables. Jednym z przypadków użycia hashtable
jest tabela przeglądowa. Jest to alternatywne podejście do wspólnego wzorca, do którego często adresuje się instrukcja switch
.
$day = 3
$lookup = @{
0 = 'Sunday'
1 = 'Monday'
2 = 'Tuesday'
3 = 'Wednesday'
4 = 'Thursday'
5 = 'Friday'
6 = 'Saturday'
}
$lookup[$day]
Wednesday
Jeśli używam tylko switch
do wyszukiwania, często używam hashtable
.
Enum
Program PowerShell 5.0 wprowadził funkcję enum
, która jest także dostępna w tym przypadku.
$day = 3
enum DayOfTheWeek {
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
}
[DayOfTheWeek]$day
Wednesday
Moglibyśmy spędzić cały dzień, przyglądając się różnym sposobom rozwiązania tego problemu. Chciałem tylko upewnić się, że wiesz, że masz opcje.
Końcowe wyrazy
Instrukcja switch na pierwszy rzut oka jest prosta, ale oferuje zaawansowane funkcje, których istnienia większość ludzi sobie nie uświadamia. Łączenie tych funkcji sprawia, że jest to zaawansowana funkcja. Mam nadzieję, że nauczyłeś się czegoś, czego wcześniej nie zdałeś sobie sprawy.