Tudo o que você sempre quis saber sobre a declaração de switch
Como muitas outras linguagens, o PowerShell tem comandos para controlar o fluxo de execução em seus scripts. Uma dessas instruções é a instrução switch e, no PowerShell, oferece recursos que não são encontrados em outros idiomas. Hoje, mergulhamos fundo no trabalho com o PowerShell switch
.
Nota
A versão original deste artigo apareceu no blog escrito por @KevinMarquette. A equipe do PowerShell agradece Kevin por compartilhar esse conteúdo conosco. Por favor, confira seu blog em PowerShellExplained.com.
A if
declaração
Uma das primeiras afirmações que você aprende é a if
declaração. Ele permite executar um bloco de script se uma instrução for $true
.
if ( Test-Path $Path )
{
Remove-Item $Path
}
Você pode ter uma lógica muito mais complicada usando elseif
e else
instruções. Aqui está um exemplo em que tenho um valor numérico para o dia da semana e quero obter o nome como uma cadeia de caracteres.
$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
Acontece que este é um padrão comum e há muitas maneiras de lidar com isso. Um deles é com um switch
arquivo .
Instrução Switch
A switch
instrução permite que você forneça uma variável e uma lista de valores possíveis. Se o valor corresponder à variável, seu scriptblock será executado.
$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'
Neste exemplo, o valor de $day
corresponde a um dos valores numéricos e, em seguida, o nome correto é atribuído a $result
. Estamos fazendo apenas uma atribuição variável neste exemplo, mas qualquer PowerShell pode ser executado nesses blocos de script.
Atribuir a uma variável
Podemos escrever este último exemplo de outra forma.
$result = switch ( $day )
{
0 { 'Sunday' }
1 { 'Monday' }
2 { 'Tuesday' }
3 { 'Wednesday' }
4 { 'Thursday' }
5 { 'Friday' }
6 { 'Saturday' }
}
Estamos colocando o valor no pipeline do PowerShell e atribuindo-o ao $result
. Você pode fazer a mesma coisa com as if
declarações e foreach
.
Predefinido
Podemos usar a default
palavra-chave para identificar o que deve acontecer se não houver correspondência.
$result = switch ( $day )
{
0 { 'Sunday' }
# ...
6 { 'Saturday' }
default { 'Unknown' }
}
Aqui retornamos o valor Unknown
no caso padrão.
Cadeias
Eu estava combinando números nesses últimos exemplos, mas você também pode combinar strings.
$item = 'Role'
switch ( $item )
{
Component
{
'is a component'
}
Role
{
'is a role'
}
Location
{
'is a location'
}
}
is a role
Decidi não embrulhar o Component
,Role
e Location
as correspondências entre aspas aqui para destacar que elas são opcionais. O switch
trata esses como uma corda na maioria dos casos.
Matrizes
Um dos recursos interessantes do PowerShell switch
é a maneira como ele lida com matrizes. Se você fornecer uma switch
matriz, ela processará cada elemento dessa coleção.
$roles = @('WEB','Database')
switch ( $roles ) {
'Database' { 'Configure SQL' }
'WEB' { 'Configure IIS' }
'FileServer' { 'Configure Share' }
}
Configure IIS
Configure SQL
Se você tiver itens repetidos em sua matriz, eles serão correspondidos várias vezes pela seção apropriada.
PSItem
Você pode usar o $PSItem
ou $_
para fazer referência ao item atual que foi processado. Quando fazemos uma partida simples, $PSItem
é o valor que estamos a corresponder. Vou realizar algumas partidas avançadas na próxima seção onde essa variável é usada.
Parâmetros
Um recurso exclusivo do PowerShell switch
é que ele tem vários parâmetros de switch que alteram seu desempenho.
-Sensível a maiúsculas e minúsculas
As correspondências não diferenciam maiúsculas de minúsculas por padrão. Se precisar diferenciar maiúsculas de minúsculas, você pode usar -CaseSensitive
o . Isso pode ser usado em combinação com os outros parâmetros do switch.
-Curinga
Podemos ativar o suporte a curingas com o -wildcard
switch. Isso usa a mesma lógica curinga que o -like
operador para fazer cada correspondência.
$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
Aqui estamos processando uma mensagem e, em seguida, enviando-a em diferentes fluxos com base no conteúdo.
-Regex
A instrução switch suporta correspondências regex da mesma forma que curinga.
switch -Regex ( $message )
{
'^Error'
{
Write-Error -Message $Message
}
'^Warning'
{
Write-Warning -Message $Message
}
default
{
Write-Information $message
}
}
Tenho mais exemplos de uso de regex em outro artigo que escrevi: As muitas maneiras de usar regex.
-Arquivo
Uma característica pouco conhecida da instrução switch é que ele pode processar um arquivo com o -File
parâmetro. Você usa -file
com um caminho para um arquivo em vez de dar-lhe uma expressão variável.
switch -Wildcard -File $path
{
'Error*'
{
Write-Error -Message $PSItem
}
'Warning*'
{
Write-Warning -Message $PSItem
}
default
{
Write-Output $PSItem
}
}
Funciona como processar uma matriz. Neste exemplo, combino-o com a correspondência curinga e uso o $PSItem
. Isso processaria um arquivo de log e o converteria em mensagens de aviso e erro, dependendo das correspondências de regex.
Detalhes avançados
Agora que você está ciente de todos esses recursos documentados, podemos usá-los no contexto de processamento mais avançado.
Expressões
O switch
pode estar em uma expressão em vez de uma variável.
switch ( ( Get-Service | Where status -eq 'running' ).name ) {...}
Qualquer que seja a avaliação da expressão é o valor usado para a correspondência.
Correspondências múltiplas
Você pode já ter percebido isso, mas um switch
pode corresponder a várias condições. Isto é especialmente verdadeiro quando se usa -wildcard
ou -regex
combina. Você pode adicionar a mesma condição várias vezes e todas são acionadas.
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
Todas essas três declarações são demitidas. Isso mostra que todas as condições são verificadas (em ordem). Isso vale para matrizes de processamento em que cada item verifica cada condição.
Continuar
Normalmente, é aqui que eu introduziria a break
declaração, mas é melhor que aprendamos a usar continue
primeiro. Assim como com um foreach
loop, continue
continua para o próximo item da coleção ou sai do switch
se não houver mais itens. Podemos reescrever esse último exemplo com instruções continue para que apenas uma instrução seja executada.
switch ( 'Word' )
{
'word'
{
'lower case word match'
continue
}
'Word'
{
'mixed case word match'
continue
}
'WORD'
{
'upper case word match'
continue
}
}
lower case word match
Em vez de corresponder aos três itens, o primeiro é correspondido e o switch continua para o próximo valor. Como não há mais valores para processar, o switch é encerrado. Este próximo exemplo mostra como um curinga pode corresponder a vários itens.
switch -Wildcard -File $path
{
'*Error*'
{
Write-Error -Message $PSItem
continue
}
'*Warning*'
{
Write-Warning -Message $PSItem
continue
}
default
{
Write-Output $PSItem
}
}
Como uma linha no arquivo de entrada pode conter a palavra Error
e Warning
, queremos apenas que a primeira seja executada e, em seguida, continue processando o arquivo.
Pausa
Uma break
instrução sai do switch. Este é o mesmo comportamento que continue
se apresenta para valores únicos. A diferença é mostrada ao processar uma matriz. break
interrompe todo o processamento no interruptor e continue
passa para o próximo item.
$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
Neste caso, se acertarmos qualquer linha que comece com Error
, então obtemos um erro e o interruptor para.
É isso que essa break
declaração está a fazer por nós. Se encontrarmos Error
dentro da corda e não apenas no início, escrevemos como um aviso. Fazemos a mesma coisa para Warning
. É possível que uma linha possa ter a palavra Error
e Warning
, mas só precisamos de uma para processar. É isso que a declaração está a continue
fazer por nós.
Quebrar rótulos
A switch
declaração suporta break/continue
rótulos como 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
}
}
}
Eu pessoalmente não gosto do uso de rótulos de quebra, mas eu queria apontá-los porque eles são confusos se você nunca os viu antes. Quando você tem várias switch
instruções ou foreach
que estão aninhadas, você pode querer quebrar de mais do que o item mais interno. Você pode colocar um rótulo em um switch
que pode ser o alvo do seu break
.
Enumeração
O PowerShell 5.0 nos deu enums e podemos usá-los em um switch.
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 você quiser manter tudo como enums fortemente tipados, então você pode colocá-los entre parênteses.
switch ($item )
{
([Context]::Component)
{
'is a component'
}
([Context]::Role)
{
'is a role'
}
([Context]::Location)
{
'is a location'
}
}
Os parênteses são necessários aqui para que o switch não trate o valor [Context]::Location
como uma cadeia de caracteres literal.
ScriptBlock
Podemos usar um scriptblock para realizar a avaliação de uma correspondência, se necessário.
$age = 37
switch ( $age )
{
{$PSItem -le 18}
{
'child'
}
{$PSItem -gt 18}
{
'adult'
}
}
'adult'
Isso aumenta a complexidade e pode dificultar a switch
leitura. Na maioria dos casos em que você usaria algo assim, seria melhor usar if
e elseif
declarações. Eu consideraria usar isso se eu já tivesse um grande interruptor no lugar e eu precisasse de dois itens para acertar o mesmo bloco de avaliação.
Uma coisa que eu acho que ajuda na legibilidade é colocar o scriptblock entre parênteses.
switch ( $age )
{
({$PSItem -le 18})
{
'child'
}
({$PSItem -gt 18})
{
'adult'
}
}
Ele ainda executa da mesma maneira e dá uma melhor pausa visual ao olhar rapidamente para ele.
Regex $matches
Precisamos revisitar o regex para tocar em algo que não é imediatamente óbvio. O uso de regex preenche a $matches
variável. Eu entro no uso de mais quando eu falo sobre as muitas maneiras de $matches
usar regex. Aqui está um exemplo rápido para mostrá-lo em ação com correspondências nomeadas.
$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
Você pode corresponder a um $null
valor que não precisa ser o padrão.
$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
Ao testar uma cadeia de caracteres vazia em uma switch
instrução, é importante usar a instrução de comparação como mostrado neste exemplo em vez do valor ''
bruto. Em uma switch
instrução, o valor ''
bruto também corresponde a $null
. Por exemplo:
$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
Além disso, tenha cuidado com retornos vazios de cmdlets. Os cmdlets ou pipelines que não têm saída são tratados como uma matriz vazia que não corresponde a nada, incluindo o default
caso.
$file = Get-ChildItem NonExistantFile*
switch ( $file )
{
$null { '$file is $null' }
default { "`$file is type $($file.GetType().Name)" }
}
# No matches
Expressão constante
Lee Dailey apontou que podemos usar uma expressão constante $true
para avaliar [bool]
itens.
Imagine se tivermos várias verificações booleanas que precisam acontecer.
$isVisible = $false
$isEnabled = $true
$isSecure = $true
switch ( $true )
{
$isEnabled
{
'Do-Action'
}
$isVisible
{
'Show-Animation'
}
$isSecure
{
'Enable-AdminMenu'
}
}
Do-Action
Enabled-AdminMenu
Esta é uma maneira limpa de avaliar e agir sobre o status de vários campos booleanos. O legal disso é que você pode fazer com que uma correspondência inverta o status de um valor que ainda não foi avaliado.
$isVisible = $false
$isEnabled = $true
$isAdmin = $false
switch ( $true )
{
$isEnabled
{
'Do-Action'
$isVisible = $true
}
$isVisible
{
'Show-Animation'
}
$isAdmin
{
'Enable-AdminMenu'
}
}
Do-Action
Show-Animation
A configuração $isEnabled
como $true
neste exemplo garante que $isVisible
também esteja definida como $true
.
Em seguida, quando $isVisible
é avaliado, seu scriptblock é invocado. Isso é um pouco contraintuitivo, mas é um uso inteligente da mecânica.
$switch variável automática
Quando o switch
está processando seus valores, ele cria um enumerador e o $switch
chama de . Essa é uma variável automática criada pelo PowerShell e você pode manipulá-la diretamente.
$a = 1, 2, 3, 4
switch($a) {
1 { [void]$switch.MoveNext(); $switch.Current }
3 { [void]$switch.MoveNext(); $switch.Current }
}
Isto dá-lhe os resultados de:
2
4
Ao mover o enumerador para frente, o próximo item não é processado switch
pelo mas você pode acessar esse valor diretamente. Eu chamaria isso de loucura.
Outros padrões
Hashtables
Um dos meus posts mais populares é o que fiz em hashtables. Um dos casos de uso para um hashtable
é ser uma tabela de pesquisa. Essa é uma abordagem alternativa a um padrão comum que uma switch
declaração geralmente aborda.
$day = 3
$lookup = @{
0 = 'Sunday'
1 = 'Monday'
2 = 'Tuesday'
3 = 'Wednesday'
4 = 'Thursday'
5 = 'Friday'
6 = 'Saturday'
}
$lookup[$day]
Wednesday
Se eu estiver usando apenas um switch
como uma pesquisa, eu costumo usar um hashtable
em vez disso.
Enumeração
O PowerShell 5.0 introduziu o Enum
e também é uma opção neste caso.
$day = 3
enum DayOfTheWeek {
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
}
[DayOfTheWeek]$day
Wednesday
Poderíamos ir o dia todo procurando diferentes maneiras de resolver esse problema. Eu só queria ter certeza de que você sabia que tinha opções.
Palavras finais
A instrução switch é simples na superfície, mas oferece alguns recursos avançados que a maioria das pessoas não percebe que estão disponíveis. Encadear esses recursos juntos torna esse um recurso poderoso. Espero que você tenha aprendido algo que você não tinha percebido antes.
Comentários
https://aka.ms/ContentUserFeedback.
Brevemente: Ao longo de 2024, vamos descontinuar progressivamente o GitHub Issues como mecanismo de feedback para conteúdos e substituí-lo por um novo sistema de feedback. Para obter mais informações, veja:Submeter e ver comentários