Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Analogamente a molti altri linguaggi, PowerShell include comandi per controllare il flusso di esecuzione all'interno degli script. Una di queste istruzioni è l'istruzione switch e in PowerShell offre funzionalità non disponibili in altri linguaggi. Oggi viene illustrato in modo approfondito l'uso di PowerShell switch
.
Nota
La versione originale di questo articolo è apparsa nel blog scritto da @KevinMarquette. Il team di PowerShell ringrazia Kevin per aver condiviso questo contenuto con noi. Consultare il suo blog all'indirizzo PowerShellExplained.com.
Istruzione if
Una delle prime istruzioni che si imparano è la dichiarazione if
. Consente di eseguire un blocco di script se un'istruzione è $true
.
if ( Test-Path $Path )
{
Remove-Item $Path
}
È possibile avere una logica molto più complessa usando elseif
e istruzioni else
. Di seguito è riportato un esempio in cui è presente un valore numerico per il giorno della settimana e si vuole ottenere il nome come stringa.
$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
Si scopre che questo è un modello comune e ci sono molti modi per gestire questo problema. Uno di essi è con un switch
.
Istruzione switch
L'istruzione switch
consente di fornire una variabile e un elenco di valori possibili. Se il valore corrisponde alla variabile, viene eseguito il relativo scriptblock.
$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'
Per questo esempio, il valore di $day
corrisponde a uno dei valori numerici, quindi il nome corretto viene assegnato a $result
. In questo esempio viene eseguita solo un'assegnazione di variabile, ma qualsiasi PowerShell può essere eseguito in tali blocchi di script.
Assegnare a una variabile
È possibile scrivere l'ultimo esempio in un altro modo.
$result = switch ( $day )
{
0 { 'Sunday' }
1 { 'Monday' }
2 { 'Tuesday' }
3 { 'Wednesday' }
4 { 'Thursday' }
5 { 'Friday' }
6 { 'Saturday' }
}
Si inserisce il valore nella pipeline di PowerShell e lo si assegna al $result
. È possibile eseguire questa stessa operazione con le istruzioni if
e foreach
.
Impostazione predefinita
È possibile usare la parola chiave default
per identificare ciò che deve accadere se non esiste alcuna corrispondenza.
$result = switch ( $day )
{
0 { 'Sunday' }
# ...
6 { 'Saturday' }
default { 'Unknown' }
}
In questo caso viene restituito il valore Unknown
nel caso predefinito.
Stringhe
Stavo abbinando numeri in quegli ultimi esempi, ma puoi anche abbinare stringhe.
$item = 'Role'
switch ( $item )
{
Component
{
'is a component'
}
Role
{
'is a role'
}
Location
{
'is a location'
}
}
is a role
Ho deciso di non inserire tra virgolette le corrispondenze Component
,Role
e Location
qui per evidenziare che sono facoltative. La switch
considera tali come una stringa nella maggior parte dei casi.
Matrici
Una delle funzionalità interessanti del switch
di PowerShell è il modo in cui gestisce le matrici. Se dai al switch
una matrice, elabora ogni elemento di quella raccolta.
$roles = @('WEB','Database')
switch ( $roles ) {
'Database' { 'Configure SQL' }
'WEB' { 'Configure IIS' }
'FileServer' { 'Configure Share' }
}
Configure IIS
Configure SQL
Se nella matrice sono presenti elementi ripetuti, questi vengono confrontati più volte dalla sezione appropriata.
PSItem
Puoi utilizzare il $PSItem
o il $_
per fare riferimento all'elemento corrente che è stato elaborato. Quando facciamo un semplice confronto, $PSItem
è il valore che stiamo confrontando. Eseguirò alcuni confronti avanzati nella sezione successiva in cui viene utilizzata questa variabile.
Parametri
Una funzionalità univoca del switch
di PowerShell è che include diversi parametri switch che cambiano il modo in cui opera.
-CaseSensitive
Le corrispondenze non fanno distinzione tra maiuscole e minuscole per impostazione predefinita. Se è necessario fare distinzione tra maiuscole e minuscole, è possibile usare -CaseSensitive
. Può essere usato in combinazione con gli altri parametri switch.
-Carattere jolly
È possibile abilitare il supporto per i caratteri jolly con l'interruttore -Wildcard
. Questo utilizza la stessa logica dei caratteri jolly dell'operatore -like
per effettuare ogni corrispondenza.
$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
In questo caso viene elaborato un messaggio e quindi restituito su flussi diversi in base al contenuto.
-Regex
L'istruzione switch supporta le corrispondenze con le espressioni regolari (regex) esattamente come avviene con i caratteri jolly.
switch -Regex ( $message )
{
'^Error'
{
Write-Error -Message $Message
}
'^Warning'
{
Write-Warning -Message $Message
}
default
{
Write-Information $message
}
}
Ho più esempi di uso di regex in un altro articolo ho scritto: I molti modi per usare regex.
-File
Una funzionalità poco nota dell'istruzione switch è che può elaborare un file con il parametro -File
. È possibile usare -File
con un percorso di un file anziché assegnargli un'espressione di variabile.
switch -Wildcard -File $path
{
'Error*'
{
Write-Error -Message $PSItem
}
'Warning*'
{
Write-Warning -Message $PSItem
}
default
{
Write-Output $PSItem
}
}
Funziona esattamente come l'elaborazione di una matrice. In questo esempio, lo combino con la corrispondenza con caratteri jolly e utilizzo il $PSItem
. Questo processo elabora un file di log e lo converte in messaggi di avviso e di errore a seconda delle corrispondenze regex.
Dettagli avanzati
Ora che si è a conoscenza di tutte queste funzionalità documentate, è possibile usarle nel contesto di elaborazione più avanzata.
Espressioni
Il switch
può trovarsi in un'espressione anziché in una variabile.
switch ( ( Get-Service | where Status -EQ 'running' ).Name ) {...}
Il valore a cui l'espressione viene valutata è utilizzato per il confronto.
Più corrispondenze
Potresti aver già notato che un switch
può corrispondere a più condizioni. Ciò è particolarmente vero quando si usano le corrispondenze -Wildcard
o -Regex
. È possibile aggiungere la stessa condizione più volte e tutti vengono attivati.
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
Tutte e tre queste affermazioni sono licenziate. Ciò mostra che ogni condizione viene controllata (in ordine). Questo vale per l'elaborazione di matrici in cui ogni elemento controlla ogni condizione.
Continuare
In genere, è qui che introdurrei l'istruzione break
, ma è meglio imparare a usare continue
prima. Proprio come con un ciclo di foreach
, continue
continua con l'elemento successivo nella raccolta o esce dal switch
se non sono presenti altri elementi. È possibile riscrivere l'ultimo esempio con istruzioni continue in modo che venga eseguita una sola istruzione.
switch ( 'Word' )
{
'word'
{
'lower case word match'
continue
}
'Word'
{
'mixed case word match'
continue
}
'WORD'
{
'upper case word match'
continue
}
}
lower case word match
Anziché abbinare tutti e tre gli elementi, il primo viene abbinato e il passaggio continua al valore successivo. Poiché non sono presenti valori lasciati per l'elaborazione, l'opzione viene chiusa. Questo prossimo esempio mostra come un carattere jolly possa corrispondere a più elementi.
switch -Wildcard -File $path
{
'*Error*'
{
Write-Error -Message $PSItem
continue
}
'*Warning*'
{
Write-Warning -Message $PSItem
continue
}
default
{
Write-Output $PSItem
}
}
Poiché una riga nel file di input può contenere sia la parola Error
che Warning
, si vuole eseguire solo la prima e quindi continuare l'elaborazione del file.
Pausa
Un'istruzione break
esce dal switch. Si tratta dello stesso comportamento che continue
presenta per i singoli valori. La differenza viene mostrata durante l'elaborazione di una matrice.
break
arresta tutti i processi nel switch e continue
passa all'elemento successivo.
$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 questo caso, se si raggiungono linee che iniziano con Error
, viene visualizzato un errore e l'opzione si arresta.
Questo è ciò che l'affermazione break
sta facendo per noi. Se troviamo Error
all'interno della stringa e non solo all'inizio, lo scriviamo come avviso. Si esegue la stessa operazione per Warning
. È possibile che una riga abbia sia la parola Error
che Warning
, ma ne è necessaria una sola da elaborare. Questo è ciò che l'affermazione continue
sta facendo per noi.
Dividi etichette
L'istruzione switch
supporta etichette break/continue
esattamente come 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
}
}
}
Personalmente non mi piace l'uso di etichette di interruzione, ma volevo segnalarle perché sono confuse se non le hai mai viste prima. Quando si dispone di più istruzioni switch
o foreach
annidate, è possibile che si voglia uscire più dell'elemento più interno.When you have multiple switch
or foreach
statement that are nested, you may want to break out of more than the inner most item. È possibile applicare un'etichetta su un switch
che può essere destinato al tuo break
.
Enum
PowerShell 5.0 ha fornito enumerazioni ed è possibile usarle in un commutatore.
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
Se si desidera mantenere tutto come enumerazioni fortemente tipate, è possibile inserirli tra parentesi.
switch ($item )
{
([Context]::Component)
{
'is a component'
}
([Context]::Role)
{
'is a role'
}
([Context]::Location)
{
'is a location'
}
}
Le parentesi sono necessarie qui in modo che l'opzione non consideri il valore [Context]::Location
come stringa letterale.
ScriptBlock
È possibile usare uno scriptblock per eseguire la valutazione per una corrispondenza, se necessario.
$age = 37
switch ( $age )
{
{$PSItem -le 18}
{
'child'
}
{$PSItem -gt 18}
{
'adult'
}
}
'adult'
Questo aggiunge complessità e può rendere il tuo switch
difficile da leggere. Nella maggior parte dei casi in cui si userebbe qualcosa del genere, sarebbe preferibile utilizzare le istruzioni if
e elseif
. Prenderei in considerazione l'uso di questo se avessi già un grande interruttore sul posto e avevo bisogno di due elementi per raggiungere lo stesso blocco di valutazione.
Una cosa che penso aiuta con la leggibilità è inserire il blocco di script tra parentesi.
switch ( $age )
{
({$PSItem -le 18})
{
'child'
}
({$PSItem -gt 18})
{
'adult'
}
}
Viene comunque eseguito allo stesso modo e offre un'interruzione visiva migliore quando lo si esamina rapidamente.
Regex $Matches
Dobbiamo rivedere regex per toccare qualcosa che non è immediatamente ovvio. L'uso di regex popola la variabile $Matches
. Si passa all'uso di $Matches
di più quando si parla di I molti modi per usare regex. Ecco un esempio rapido per mostrarlo in azione con corrispondenze nominate.
$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
È possibile associare un valore $null
che non deve essere quello predefinito.
$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
Quando si testa una stringa vuota in un'istruzione switch
, è importante usare l'istruzione di confronto come illustrato in questo esempio anziché il valore non elaborato ''
. In un'istruzione switch
il valore non elaborato ''
corrisponde anche a $null
. Per esempio:
$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
Prestare inoltre attenzione ai risultati vuoti dei cmdlet. I cmdlet o le pipeline senza output vengono considerati come una matrice vuota che non corrisponde a nulla, incluso il caso default
.
$file = Get-ChildItem NonExistantFile*
switch ( $file )
{
$null { '$file is $null' }
default { "`$file is type $($file.GetType().Name)" }
}
# No matches
Espressione costante
Lee Dailey ha sottolineato che è possibile usare un'espressione di $true
costante per valutare [bool]
elementi.
Immagina di avere diversi controlli booleani che devono essere eseguiti.
$isVisible = $false
$isEnabled = $true
$isSecure = $true
switch ( $true )
{
$isEnabled
{
'Do-Action'
}
$isVisible
{
'Show-Animation'
}
$isSecure
{
'Enable-AdminMenu'
}
}
Do-Action
Enabled-AdminMenu
Si tratta di un modo pulito per valutare e intervenire sullo stato di diversi campi booleani. L'aspetto interessante di questo è che si può far sì che una corrispondenza capovolga lo stato di un valore che non è ancora stato valutato.
$isVisible = $false
$isEnabled = $true
$isAdmin = $false
switch ( $true )
{
$isEnabled
{
'Do-Action'
$isVisible = $true
}
$isVisible
{
'Show-Animation'
}
$isAdmin
{
'Enable-AdminMenu'
}
}
Do-Action
Show-Animation
L'impostazione di $isEnabled
su $true
in questo esempio garantisce che $isVisible
sia impostato anche su $true
.
Quindi, quando $isVisible
viene valutato, viene richiamato il relativo blocco di script. Si tratta di un po' controintuitivo, ma è un uso intelligente delle meccaniche.
variabile automatica $switch
Quando l'switch
elabora i valori, crea un enumeratore e lo chiama $switch
. Si tratta di una variabile automatica creata da PowerShell ed è possibile modificarla direttamente.
$a = 1, 2, 3, 4
switch($a) {
1 { [void]$switch.MoveNext(); $switch.Current }
3 { [void]$switch.MoveNext(); $switch.Current }
}
In questo modo si ottengono i risultati di:
2
4
Spostando l'enumeratore in avanti, l'elemento successivo non viene elaborato dal switch
, ma è possibile accedere direttamente a tale valore. Lo chiamerei follia.
Altri modelli
Tabelle hash
Uno dei miei post più popolari è quello che ho fatto sulle tabelle di hash . Uno dei casi d'uso per un hashtable
consiste nell'essere una tabella di ricerca. Si tratta di un approccio alternativo a un modello comune che un'istruzione switch
spesso tratta.
$day = 3
$lookup = @{
0 = 'Sunday'
1 = 'Monday'
2 = 'Tuesday'
3 = 'Wednesday'
4 = 'Thursday'
5 = 'Friday'
6 = 'Saturday'
}
$lookup[$day]
Wednesday
Se utilizzo solo un switch
per la ricerca, spesso uso un hashtable
.
Enum
PowerShell 5.0 ha introdotto il enum
ed è anche un'opzione in questo caso.
$day = 3
enum DayOfTheWeek {
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
}
[DayOfTheWeek]$day
Wednesday
Potremmo andare tutti i giorni a guardare diversi modi per risolvere questo problema. Volevo solo assicurarti che sapessi che avevi delle opzioni.
Parole finali
L'istruzione switch è semplice in superficie, ma offre alcune funzionalità avanzate che la maggior parte delle persone non sa essere disponibili. Collegare insieme queste funzionalità le rende una caratteristica potente. Spero che tu avessi imparato qualcosa che non avevi capito prima.