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 muitos outros idiomas, 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, ela oferece recursos que não são encontrados em outras linguagens. Hoje, vamos nos aprofundar no trabalho com o PowerShell switch.
Observação
A versão original deste artigo foi publicada no blog escrito por @KevinMarquette. A equipe do PowerShell agradece kevin por compartilhar este conteúdo conosco. Confira o blog dele no PowerShellExplained.com.
A instrução if
Uma das primeiras instruções que você aprende é a instrução if. 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 esse é um padrão comum e há muitas maneiras de lidar com isso. Um deles está com um switch.
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, o bloco de instrução 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'
Para este exemplo, o valor de $day corresponde a um dos valores numéricos, depois o nome correto é atribuído a $result. Estamos fazendo apenas uma atribuição de variável neste exemplo, mas qualquer PowerShell pode ser executado nesses blocos de script.
Atribuir a uma variável
Podemos escrever 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 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.
Cordas
Eu estava combinando números nesses últimos exemplos, mas você também pode combinar 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 colocar as correspondências Component, Role e Location entre aspas aqui para destacar que elas são opcionais. O switch os trata como uma cadeia de caracteres 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 nessa coleção.
$roles = @('WEB','Database')
switch ( $roles ) {
'Database' { 'Configure SQL' }
'WEB' { 'Configure IIS' }
'FileServer' { 'Configure Share' }
}
Configure IIS
Configure SQL
Se você tiver repetido itens em sua matriz, eles serão correspondidos várias vezes pela seção apropriada.
PSItem
Você pode usar o $PSItem ou o $_ para referenciar o item atual que foi processado. Quando fazemos uma correspondência simples, $PSItem é o valor que estamos tentando igualar. Executarei algumas correspondências avançadas na próxima seção em que essa variável é usada.
Parâmetros
Um recurso exclusivo do PowerShell switch é que ele tem vários [switch] parâmetros que alteram o desempenho dele.
-CaseSensitive
As correspondências não diferenciam maiúsculas de minúsculas por padrão. Se você precisar diferenciar maiúsculas de minúsculas, poderá usar -CaseSensitive. Isso pode ser usado em combinação com os outros [switch] parâmetros.
-Curinga
Podemos habilitar o suporte curinga com o -Wildcard[switch] parâmetro. Isso usa a mesma lógica curinga que o operador -like para executar 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, gerando-a em fluxos diferentes com base no conteúdo.
-Regex
A instrução switch dá suporte a correspondências regex exatamente como faz com curingas.
switch -Regex ( $message )
{
'^Error'
{
Write-Error -Message $Message
}
'^Warning'
{
Write-Warning -Message $Message
}
default
{
Write-Information $message
}
}
Tenho mais exemplos de uso do regex em outro artigo que escrevi: as muitas maneiras de usar regex.
-Arquivo
Um recurso pouco conhecido 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
}
}
Ele funciona como processar uma matriz. Neste exemplo, eu o combino com a correspondência curinga e utilizo o $PSItem. Isso processaria um arquivo de log e o converteria em mensagens de aviso e erro, dependendo das correspondências regex.
Detalhes avançados
Agora que você está ciente de todos esses recursos documentados, podemos usá-los no contexto de processamento mais avançado.
Expressions
Pode switch estar em uma expressão em vez de uma variável.
switch ( ( Get-Service | where Status -EQ 'running' ).Name ) {...}
O que quer que a expressão seja avaliada é o valor usado para a correspondência.
Várias correspondências
Você talvez já tenha percebido, mas switch pode corresponder a várias condições. Isso é especialmente verdadeiro ao usar -Wildcard ou -Regex correspondências. Você pode adicionar a mesma condição várias vezes e todas são ativadas.
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 executadas. 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 apresentaria a break declaração, mas é melhor que aprendamos a usar continue primeiro. Assim como em um foreach loop, continue continua com o próximo item na coleção ou sai do switch se não houver mais itens. Podemos reescrever esse último exemplo com instruções de continuação 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 a opção continua para o próximo valor. Como não há valores a serem processados, a opção é encerrada. 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.
Interromper
Uma break instrução sai da opção. Esse é o mesmo comportamento que continue apresenta para valores únicos. A diferença é mostrada ao processar uma matriz.
break interrompe todo o processamento na opção 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 qualquer linha que comece com Error, receberemos um erro e a execução do switch será interrompida.
Isso é o que essa break declaração está fazendo por nós. Se encontrarmos Error dentro da cadeia de caracteres e não apenas no início, a escreveremos como um aviso. Fazemos a mesma coisa por Warning. É possível que uma linha possa ter a palavra Error e Warning, mas só precisamos de uma para processar. Isso é o que a continue declaração está fazendo por nós.
Fragmentar etiquetas
A instrução switch oferece 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
}
}
}
Eu pessoalmente não gosto do uso de labels de break, mas eu queria apontá-los porque eles podem ser confusos se você nunca os viu previamente. Quando você tem várias instruções switch ou foreach aninhadas, talvez queira sair de mais de um item, não apenas do mais interno. Você pode colocar uma etiqueta em um switch que pode ser o alvo do seu break.
Enum
O PowerShell 5.0 nos trouxe 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, poderá 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 a opção não trate o valor [Context]::Location como uma cadeia de caracteres literal.
Scriptblock
Podemos usar um scriptblock 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 adiciona complexidade e pode dificultar a leitura de switch. Na maioria dos casos em que você usaria algo assim, seria melhor usar if e elseif declarações. Eu consideraria usar isso se já tivesse um grande switch instalado e precisasse de dois itens para atingir o mesmo bloco de avaliação.
Uma coisa que eu acho que ajuda na legibilidade é colocar o bloco de scripts entre parênteses.
switch ( $age )
{
({$PSItem -le 18})
{
'child'
}
({$PSItem -gt 18})
{
'adult'
}
}
Ele ainda executa da mesma maneira e dá uma pausa visual melhor ao olhar rapidamente para ele.
$Matches de expressão regular
Precisamos revisitar o regex para abordar um ponto que não é imediatamente óbvio. O uso de regex preenche a $Matches variável. Eu me aprofundo mais no uso de $Matches quando eu falo sobre as muitas maneiras de usar regex. Aqui está um exemplo rápido para mostrá-lo em ação com correspondências identificadas.
$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 fazer uma correspondência com 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 string vazia em uma switch declaração, é importante usar a declaração de comparação, conforme mostrado neste exemplo, em vez do valor '' bruto. Em uma switch declaraçã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, preste atenção aos 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 caso default.
$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 boolianas que precisam acontecer.
$isVisible = $false
$isEnabled = $true
$isSecure = $true
switch ( $true )
{
$isEnabled
{
'Do-Action'
}
$isVisible
{
'Show-Animation'
}
$isSecure
{
'Enable-AdminMenu'
}
}
Do-Action
Enabled-AdminMenu
Essa é uma maneira limpa de avaliar e tomar medidas sobre o status de vários campos boolianos. O interessante disso é que você pode ter uma correspondência invertendo 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
Configurar $isEnabled para $true neste exemplo garante que $isVisible também seja configurado para $true.
Em seguida, quando $isVisible é avaliado, seu bloco de instrução é invocado. Isso é um pouco contra-intuitivo, mas é um uso inteligente da mecânica.
$switch variável automática
Quando ele switch está processando seus valores, ele cria um enumerador e o chama $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
Movendo o enumerador para frente, o próximo item não é processado pelo switch , mas você pode acessar esse valor diretamente. Eu chamaria de loucura.
Outros padrões
Tabelas de hash
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 instrução switch costuma tratar.
$day = 3
$lookup = @{
0 = 'Sunday'
1 = 'Monday'
2 = 'Tuesday'
3 = 'Wednesday'
4 = 'Thursday'
5 = 'Friday'
6 = 'Saturday'
}
$lookup[$day]
Wednesday
Quando eu uso switch apenas como referência, geralmente prefiro usar hashtable.
Enum
O PowerShell 5.0 introduziu o enum, e 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 todo procurando maneiras diferentes de resolver esse problema. Só queria ter certeza que sabia que tinha opções.
Palavras finais
A instrução switch pode parecer simples à primeira vista, mas oferece alguns recursos avançados que a maioria das pessoas não percebe que existem. A associação desses recursos faz com que esse recurso seja poderoso. Espero que tenha aprendido algo que não tinha percebido antes.