Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Como muitas outras linguagens, o PowerShell tem comandos para controlar o fluxo de execução dentro dos seus scripts. Uma dessas instruções é a instrução switch e, no PowerShell, ela oferece recursos que não são encontrados em outras linguagens. Hoje, vamos nos aprofundar sobre como trabalhar com o switch
do PowerShell.
Observação
A versão original deste artigo foi publicada no blog escrito por @KevinMarquette. A equipe do PowerShell agradece a Kevin por compartilhar o conteúdo conosco. Confira o blog dele em PowerShellExplained.com.
A instrução if
Uma das primeiras instruções que você aprende é a instrução if
. Ela permitirá que você execute 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 as instruções elseif
e else
. A seguir está um exemplo no qual 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
Esse é um padrão comum e há várias maneiras de lidar com isso. Uma delas é com um switch
.
Instrução switch
A instrução switch
permite que você forneça uma variável e uma lista de valores possíveis. Se o valor corresponder à variável, o bloco de script 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'
Nesse exemplo, o valor de $day
corresponde a um dos valores numéricos, em seguida, o nome correto é atribuído a $result
. Este exemplo é apenas uma atribuição de variável, mas qualquer PowerShell pode ser executado nesses blocos de script.
Atribuir a uma variável
Podemos gravar esse último exemplo de outra maneira.
$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 instruções if
e foreach
.
Padrão
Podemos usar a palavra-chave default
para identificar o que deve acontecer se não houver nenhuma correspondência.
$result = switch ( $day )
{
0 { 'Sunday' }
# ...
6 { 'Saturday' }
default { 'Unknown' }
}
Aqui, retornamos o valor Unknown
no caso padrão.
Cadeias de caracteres
Estava correspondendo números nesses últimos exemplos, mas você também pode corresponder cadeias de caracteres.
$item = 'Role'
switch ( $item )
{
Component
{
'is a component'
}
Role
{
'is a role'
}
Location
{
'is a location'
}
}
is a role
Decidi não encapsular as correspondências Component
,Role
e Location
entre aspas aqui para realçar que elas são opcionais. O switch
trata elas como uma cadeia de caracteres na maioria dos casos.
Matrizes
Um dos recursos interessantes de switch
do PowerShell é a maneira como ele lida com matrizes. Se você fornecer uma matriz a um switch
, ele processará cada elemento nessa 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 na sua matriz, eles serão correspondidos várias vezes pela seção apropriada.
PSItem
Você pode usar $PSItem
ou $_
para fazer referência ao item atual que foi processado. Quando fazemos uma correspondência simples, $PSItem
é o valor que estamos correspondendo. Executarei algumas correspondências avançadas na próxima seção nas quais essa variável é usada.
Parâmetros
Um recurso exclusivo de switch
do PowerShell é que ele tem um número de parâmetros de opção que alteram a forma como ele é executado.
-CaseSensitive
Por padrão, as correspondências não diferenciam maiúsculas de minúsculas. Se você precisar diferenciar maiúsculas de minúsculas, poderá usar -CaseSensitive
. Isso pode ser usado em combinação com os outros parâmetros de opção.
-Wildcard
Podemos habilitar o suporte a curinga com a switch -wildcard
. Isso usa a mesma lógica de curinga que o operador -like
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 colocando a saída dela em fluxos diferentes com base no conteúdo.
-Regex
A instrução switch dá suporte a correspondências de expressão regular, exatamente como acontece com curingas.
switch -Regex ( $message )
{
'^Error'
{
Write-Error -Message $Message
}
'^Warning'
{
Write-Warning -Message $Message
}
default
{
Write-Information $message
}
}
Há mais exemplos de uso de expressões regulares em outro artigo que escrevi: As várias maneiras de usar regex.
-File
Um recurso pouco conhecido da instrução switch é que ela pode processar um arquivo com o parâmetro -File
. Você usa -file
com um caminho para um arquivo em vez de dar a ele uma expressão variável.
switch -Wildcard -File $path
{
'Error*'
{
Write-Error -Message $PSItem
}
'Warning*'
{
Write-Warning -Message $PSItem
}
default
{
Write-Output $PSItem
}
}
Ele funciona exatamente como processar uma matriz. Nesse exemplo, eu o combino com uma correspondência de curinga e uso o $PSItem
. Isso processaria um arquivo de log e o converteria em mensagens erro e de aviso, dependendo das correspondências de expressão regular.
Detalhes avançados
Agora que você está ciente de todos esses recursos documentados, podemos usá-los no contexto de um processamento mais avançado.
Expressões
O switch
pode estar em uma expressão em vez de em uma variável.
switch ( ( Get-Service | Where status -eq 'running' ).name ) {...}
Independentemente de como a expressão é avaliada, ela é o valor usado para a correspondência.
Múltiplas correspondências
Talvez você já tenha percebido, mas um switch
pode corresponder a várias condições. Isso é especialmente verdadeiro ao usar as correspondências -wildcard
ou -regex
. Você pode adicionar a mesma condição várias vezes e todas elas serem disparadas.
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
Essas três instruções são disparadas. Isso mostra que todas as condições são verificadas (em ordem). Isso é verdadeiro para matrizes de processamento, nas quais cada item verifica cada condição.
Continue
Normalmente, aqui que eu apresentaria a instrução break
, mas é melhor que aprendemos a usar a continue
primeiro. Assim como com um loop de foreach
, o continue
continua no 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 todos os três itens, o primeiro é correspondido e a switch continua para o próximo valor. Como não há valores restantes para processar, switch é fechada. 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 as palavras Error
e Warning
, queremos que apenas a primeira seja executada e, em seguida, continue processando o arquivo.
Interromper
Uma instrução break
sai da switch. Esse é o mesmo comportamento que o continue
apresenta para valores únicos. A diferença é mostrada durante o processamento de uma matriz. break
interrompe todo o processamento na switch 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
Nesse caso, se atingirmos em alguma linha que começa com Error
, obteremos um erro e a switch será interrompida.
Isso é o que essa instrução break
está fazendo para nós. Se encontrarmos Error
dentro da cadeia de caracteres e não apenas no início, vamos gravá-lo como um aviso. Faremos a mesma coisa para Warning
. É possível que uma linha tenha ambas as palavras Error
e Warning
, mas precisamos apenas de uma para processar. Isso é o que a instrução continue
está fazendo para nós.
Rótulos de intervalo
A instrução switch
dá suporte a rótulos break/continue
, assim 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
}
}
}
Pessoalmente, não gosto do uso de rótulos de intervalo, mas queria destacá-los porque eles são confusos se você nunca os viu antes. Quando você tiver várias instruções switch
ou foreach
aninhadas, talvez queira interromper mais do que o item mais interno. Você pode colocar um rótulo em um switch
que pode ser o destino do seu break
.
Enum
O PowerShell 5.0 forneceu enumerações e podemos usá-las em uma 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ê desejar manter tudo como enumerações fortemente tipadas, poderá colocá-las 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 a switch não trate o valor [Context]::Location
como uma cadeia de caracteres literal.
Bloco de script
Podemos usar um bloco de script para executar 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 tornar o seu switch
difícil de ler. Na maioria dos casos em que você usaria algo semelhante a isso, seria melhor usar as instruções if
e elseif
. Eu consideraria o uso desse recurso se já tivesse uma instrução switch muito longa em vigor e precisasse que dois itens atingissem o mesmo bloco de avaliação.
Uma coisa que acho que me ajuda com a legibilidade é colocar o bloco de script entre parênteses.
switch ( $age )
{
({$PSItem -le 18})
{
'child'
}
({$PSItem -gt 18})
{
'adult'
}
}
Ele ainda é executado da mesma maneira e fornece uma quebra visual melhor ao olhar rapidamente.
$matches de expressão regular
Precisamos revisitar as expressões regulares para discutir algo que não é imediatamente óbvio. O uso de expressões regulares popula a variável $matches
. Eu me aprofundo mais sobre o uso do $matches
ao falar sobre As várias maneiras de usar regex. A seguir 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 um valor $null
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 instrução switch
, é importante usar a instrução de comparação, conforme mostrado neste exemplo, em vez do valor bruto ''
. Em uma instrução switch
, 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 os retornos vazios dos cmdlets. 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 caso default
.
$file = Get-ChildItem NonExistantFile*
switch ( $file )
{
$null { '$file is $null' }
default { "`$file is type $($file.GetType().Name)" }
}
# No matches
Expressão de constante
Lee Dailey apontou que podemos usar uma expressão de constante $true
para avaliar os itens [bool]
.
Imagine se tivermos várias verificações boolianas que precisam ocorrer.
$isVisible = $false
$isEnabled = $true
$isSecure = $true
switch ( $true )
{
$isEnabled
{
'Do-Action'
}
$isVisible
{
'Show-Animation'
}
$isSecure
{
'Enable-AdminMenu'
}
}
Do-Action
Enabled-AdminMenu
Essa é um modo limpo de avaliar e agir sobre o status de vários campos boolianos. O interessante nisso é que uma correspondência pode inverter 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 de $isEnabled
para $true
nesse exemplo garante que $isVisible
também seja definido como $true
.
Em seguida, quando $isVisible
é avaliado, o bloco de script dele é invocado. Isso é um pouco contraintuitivo, mas é um uso inteligente da mecânica.
Variável automática $switch
Quando o switch
estiver processando os valores, ele criará um enumerador e o chamará de $switch
. 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 }
}
Isso fornece os resultados de:
2
4
Ao mover o enumerador para frente, o próximo item não é processado pelo switch
, mas você pode acessar esse valor diretamente. Eu chamaria isso de loucura.
Outros padrões
Tabelas de hash
Uma das minhas postagens mais populares é aquela que fiz sobre tabelas de hash. Um dos casos de uso de um hashtable
é ser uma tabela de pesquisa. Essa é uma abordagem alternativa para um padrão comum que uma instrução switch
geralmente aborda.
$day = 3
$lookup = @{
0 = 'Sunday'
1 = 'Monday'
2 = 'Tuesday'
3 = 'Wednesday'
4 = 'Thursday'
5 = 'Friday'
6 = 'Saturday'
}
$lookup[$day]
Wednesday
Se estiver usando um switch
apenas como uma pesquisa, geralmente usarei um hashtable
em vez disso.
Enum
O PowerShell 5.0 apresentou o Enum
e ele também é uma opção nesse caso.
$day = 3
enum DayOfTheWeek {
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
}
[DayOfTheWeek]$day
Wednesday
Poderíamos passar o dia analisando diferentes maneiras de resolver esse problema. Queria apenas me certificar de que você sabe que tem opções.
Conclusão
A instrução switch é aparentemente simples, mas oferece alguns recursos avançados que a maioria das pessoas não percebe que estão disponíveis. Juntar esses recursos torna esse um recurso poderoso. Espero que você tenha aprendido algo que não tinha percebido antes.