Compartilhar via


Tudo o que você queria saber sobre a declaração if

Como muitas outras linguagens, o PowerShell tem instruções para executar condicionalmente o código em seus scripts. Uma dessas instruções é a instrução If. Hoje, vamos nos aprofundar em um dos comandos mais fundamentais do PowerShell.

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.

Execução condicional

Seus scripts geralmente precisam tomar decisões e executar uma lógica diferente com base nessas decisões. Isso é o que quero dizer com execução condicional. Você tem uma instrução ou valor a ser avaliado e, em seguida, executa uma seção diferente do código com base nessa avaliação. Isso é exatamente o que a instrução if faz.

A instrução if

Aqui está um exemplo básico da instrução if:

$condition = $true
if ( $condition )
{
    Write-Output "The condition was true"
}

A primeira coisa que a instrução if faz é avaliar a expressão entre parênteses. Se for avaliada como $true, executará o scriptblock nas chaves. Se o valor fosse $false, ele pularia esse bloco de scripts.

No exemplo anterior, a instrução if estava apenas avaliando a variável $condition. Foi $true e teria executado o comando Write-Output dentro do scriptblock.

Em alguns idiomas, você pode colocar uma única linha de código após a instrução if e ela é executada. Esse não é o caso no PowerShell. Você deverá fornecer um scriptblock completo com chaves para funcionar corretamente.

Operadores de comparação

O uso mais comum da instrução if é comparar dois itens entre si. O PowerShell tem operadores especiais para diferentes cenários de comparação. Quando você usa um operador de comparação, o valor no lado esquerdo é comparado ao valor no lado direito.

-eq para igualdade

O -eq faz uma verificação de igualdade entre dois valores para garantir que eles sejam iguais um ao outro.

$value = Get-MysteryValue
if ( 5 -eq $value )
{
    # do something
}

Neste exemplo, estou pegando um valor conhecido de 5 e comparando-o ao meu $value para ver se eles correspondem.

Um possível caso de uso é verificar o status de um valor antes de executar uma ação nele. Você pode obter um serviço e verificar se o status estava definido como em execução antes de chamar Restart-Service.

É comum em outras linguagens, como C#, usar == para igualdade (por exemplo, 5 == $value), mas isso não funciona com o PowerShell. Outro erro comum que as pessoas cometem é usar o sinal de igual (por exemplo: 5 = $value) reservado para atribuir valores a variáveis. Ao colocar seu valor conhecido à esquerda, fica mais difícil cometer esse erro.

Este operador (e outros) tem algumas variações.

  • -eq igualdade que não diferencia maiúsculas e minúsculas
  • -ieq igualdade que não diferencia maiúsculas e minúsculas
  • -ceq igualdade que diferencia maiúsculas e minúsculas

-ne diferente de

Muitos operadores têm um operador relacionado que está verificando o resultado oposto. -ne verifica se os valores não são iguais uns aos outros.

if ( 5 -ne $value )
{
    # do something
}

Use isso para garantir que a ação seja executada somente se o valor não for 5. Um bom caso de uso seria verificar se um serviço estava em execução antes de tentar iniciá-lo.

Variações de :

  • -ne não igualar maiúsculas de minúsculas
  • -ine não igualar maiúsculas de minúsculas
  • -cne não igualar diferenciação de maiúsculas de minúsculas

Estas são variações inversas de -eq. Agruparei esses tipos ao listar variações para outros operadores.

-gt -ge -lt -le para maior que ou menor que

Esses operadores são usados ao verificar se um valor é maior ou menor que outro valor. Os -gt -ge -lt -le são GreaterThan, GreaterThanOrEqual, LessThan e LessThanOrEqual.

if ( $value -gt 5 )
{
    # do something
}

Variações de :

  • -gt maior que
  • -igt maior que, não diferencia maiúsculas de minúsculas
  • -cgt maior que, diferencia maiúsculas de minúsculas
  • -ge maior que ou igual a
  • -ige maior que ou igual a, não diferencia maiúsculas de minúsculas
  • -cge maior que ou igual a, diferencia maiúsculas de minúsculas
  • -lt menor que
  • -ilt menor que, não diferencia maiúsculas de minúsculas
  • -clt menor que, diferencia maiúsculas de minúsculas
  • -le menor que ou igual a
  • -ile menor que ou igual a, não diferencia maiúsculas de minúsculas
  • -cle menor que ou igual a, diferencia maiúsculas de minúsculas

Não sei por que você usaria opções que diferenciam e não diferenciam maiúsculas de minúsculas para esses operadores.

-like correspondências de caractere curinga

O PowerShell tem sua própria sintaxe de correspondência de padrões baseada em curinga e você pode usá-la com o operador -like. Esses padrões de caractere curinga são bastante básicos.

  • ? corresponde a qualquer caractere único
  • * corresponde a qualquer número de caracteres
$value = 'S-ATX-SQL01'
if ( $value -like 'S-*-SQL??')
{
    # do something
}

É importante ressaltar que o padrão corresponde à cadeia de caracteres inteira. Se você precisar corresponder a algo no meio da cadeia de caracteres, será necessário colocar o * em ambas as extremidades da cadeia de caracteres.

$value = 'S-ATX-SQL02'
if ( $value -like '*SQL*')
{
    # do something
}

Variações de :

  • -like caractere curinga que não diferencia maiúsculas de minúsculas
  • -ilike caractere curinga que não diferencia maiúsculas de minúsculas
  • -clike caractere curinga que diferencia maiúsculas de minúsculas
  • -notlike caractere curinga que não diferencia maiúsculas e minúsculas não correspondente
  • -inotlike caractere curinga que não diferencia maiúsculas e minúsculas não correspondente
  • -cnotlike caractere curinga que diferencia maiúsculas e minúsculas não correspondente

-match expressão regular

O operador -match permite que você verifique uma cadeia de caracteres para uma correspondência baseada em expressão regular. Use quando os padrões de caractere curinga não são flexíveis o suficiente.

$value = 'S-ATX-SQL01'
if ( $value -match 'S-\w\w\w-SQL\d\d')
{
    # do something
}

Um padrão regex corresponde a qualquer lugar da cadeia de caracteres por padrão. Portanto, você pode especificar uma substring de caracteres que você deseja corresponder, por exemplo:

$value = 'S-ATX-SQL01'
if ( $value -match 'SQL')
{
    # do something
}

Regex é uma linguagem complexa própria e vale a pena examinar. Falo mais sobre -match e as várias maneiras de usar o regex em outro artigo.

Variações de :

  • -match regex que não diferencia maiúsculas e minúsculas
  • -imatch regex que não diferencia maiúsculas e minúsculas
  • -cmatch regex que diferencia maiúsculas e minúsculas
  • -notmatch regex que não diferencia maiúsculas e minúsculas não correspondente
  • -inotmatch regex que não diferencia maiúsculas e minúsculas não correspondente
  • -cnotmatch regex que diferencia maiúsculas e minúsculas não correspondente

-is é tipo

Você pode verificar o tipo de um valor com o operador -is.

if ( $value -is [string] )
{
    # do something
}

Você pode usá-lo se estiver trabalhando com classes ou aceitando vários objetos por meio do pipeline. Você pode ter um serviço ou um nome de serviço como entrada. Verifique se você tem um serviço e busque-o apenas com o nome.

if ( $Service -isnot [System.ServiceProcess.ServiceController] )
{
    $Service = Get-Service -Name $Service
}

Variações de :

  • -is do tipo
  • -isnot não é do tipo

Operadores de coleção

Quando você usa os operadores anteriores com um único valor, o resultado é $true ou $false. Isso é tratado de forma ligeiramente diferente ao trabalhar com uma coleção. Cada item da coleção é avaliado e o operador retorna todos os valores que são avaliados como $true.

PS> 1,2,3,4 -eq 3
3

Isso ainda funciona corretamente em uma instrução if. Portanto, um valor é retornado pelo operador e, em seguida, toda a instrução é $true.

$array = 1..6
if ( $array -gt 3 )
{
    # do something
}

Há uma pequena armadilha escondida nos detalhes que preciso apontar. Ao usar o operador -ne dessa forma, é fácil interpretar a lógica de forma invertida. Usar -ne com uma coleção retornará $true se qualquer item na coleção não corresponder ao seu valor.

PS> 1,2,3 -ne 4
1
2
3

Isso pode parecer um truque inteligente, mas temos operadores -contains e -in que lidam com isso com mais eficiência. E -notcontains faz o que você espera.

-contains

O operador -contains procura seu valor na coleção. Assim que encontra uma correspondência, retorna $true.

$array = 1..6
if ( $array -contains 3 )
{
    # do something
}

Essa é a maneira preferida de ver se uma coleção contém seu valor. O uso de Where-Object (ou -eq) percorre a lista inteira toda vez e é significativamente mais lento.

Variações de :

  • -contains correspondência que não diferencia maiúsculas e minúsculas
  • -icontains correspondência que não diferencia maiúsculas e minúsculas
  • -ccontains correspondência que diferencia maiúsculas e minúsculas
  • -notcontains não correspondência que não diferencia maiúsculas e minúsculas
  • -inotcontains não correspondência que não diferencia maiúsculas e minúsculas
  • -cnotcontains não correspondência que diferencia maiúsculas e minúsculas

-in

O operador -in é exatamente como o operador -contains, exceto que a coleção está no lado direito.

$array = 1..6
if ( 3 -in $array )
{
    # do something
}

Variações de :

  • -in correspondência que não diferencia maiúsculas e minúsculas
  • -iin correspondência que não diferencia maiúsculas e minúsculas
  • -cin correspondência que diferencia maiúsculas e minúsculas
  • -notin não correspondência que não diferencia maiúsculas e minúsculas
  • -inotin não correspondência que não diferencia maiúsculas e minúsculas
  • -cnotin não correspondência que diferencia maiúsculas e minúsculas

Operadores lógicos

Operadores lógicos são usados para inverter ou combinar outras expressões.

-não

O operador -not inverte uma expressão de $false para $true ou de $true para $false. Aqui está um exemplo em que queremos executar uma ação quando Test-Path é $false.

if ( -not ( Test-Path -Path $path ) )

A maioria dos operadores que falamos tem uma variação em que você não precisa usar o operador -not. Mas ainda há momentos em que é útil.

! operador

Você pode usar ! como um alias para -not.

if ( -not $value ){}
if ( !$value ){}

Você pode ver ! usado mais por pessoas familiarizadas com outras linguagens, como C#. Prefiro digitá-lo porque é difícil ver quando dou uma olhada rápida nos meus scripts.

-and

Você pode combinar expressões com o operador -and. Quando você faz isso, ambos os lados precisam ser $true para que toda a expressão seja $true.

if ( ($age -gt 13) -and ($age -lt 55) )

Nesse exemplo, $age deve ter 13 anos ou mais para o lado esquerdo e menos de 55 para o lado direito. Eu adicionei parênteses extras para deixar mais claro nesse exemplo, mas eles são opcionais desde que a expressão seja simples. Aqui está o mesmo exemplo sem eles.

if ( $age -gt 13 -and $age -lt 55 )

A avaliação ocorre da esquerda para a direita. Se o primeiro item for avaliado como $false, ele sairá mais cedo e não executará a comparação correta. Isso é útil quando você precisa garantir que um valor exista antes de usá-lo. Por exemplo, Test-Path gerará um erro se você lhe der um caminho $null.

if ( $null -ne $path -and (Test-Path -Path $path) )

-or

O -or permite que você especifique duas expressões e retorne $true se uma delas for $true.

if ( $age -le 13 -or $age -ge 55 )

Assim como acontece com o operador -and, a avaliação ocorre da esquerda para a direita. Exceto que, se a primeira parte for $true, a instrução inteira será $true e não processará o restante da expressão.

Anote também como a sintaxe funciona para esses operadores. Você precisa de duas expressões separadas. Vi usuários tentarem fazer algo assim $value -eq 5 -or 6 sem perceber o erro.

-xor ou exclusivo

Este é um pouco incomum. -xor permite que apenas uma expressão seja avaliada como $true. Portanto, se ambos os itens forem $false ou ambos os itens forem $true, a expressão inteira será $false. Outra maneira de analisar é que a expressão só será $true quando os resultados da expressão forem diferentes.

É raro que alguém use esse operador lógico e não consigo pensar em um bom exemplo de por que eu o usaria.

Operadores bit a bit

Os operadores bit a bit executam cálculos nos bits dentro dos valores e produzem um novo valor como resultado. Ensinar operadores bit a bit está além do escopo deste artigo. Mas aqui está a lista deles.

  • -band binário E
  • -bor binário OU
  • -bxor binário OR exclusivo
  • -bnot binário NÃO
  • -shl shift left
  • -shr shift right

Expressões do PowerShell

Podemos usar o PowerShell normal dentro da declaração condicional.

if ( Test-Path -Path $Path )

Test-Path retorna $true ou $false quando é executado. Isso também se aplica a comandos que retornam outros valores.

if ( Get-Process Notepad* )

Será avaliado como $true se houver um processo retornado e $false se não houver um processo. É perfeitamente válido usar expressões de pipeline ou outras instruções do PowerShell como esta:

if ( Get-Process | where Name -EQ Notepad )

Essas expressões podem ser combinadas entre si com os operadores -and e -or, mas talvez seja necessário usar parênteses para dividi-las em subexpressões.

if ( (Get-Process) -and (Get-Service) )

Verificando se corresponde a $null

Obter "sem resultados" ou um valor de $null é avaliado como $false na instrução if. Quando você verifica especificamente por $null, é uma melhor prática adicionar $null no lado esquerdo.

if ( $null -eq $value )

Há algumas nuances ao lidar com valores $null no PowerShell. Se você está interessado em mergulhar mais fundo, eu tenho um artigo sobre tudo o que você queria saber sobre $null.

Atribuição de variável dentro da condição

Eu quase esqueci de adicionar isso até que Prasoon Karunan V me lembrou.

if ($process=Get-Process notepad -ErrorAction Ignore) {$process} else {$false}

Normalmente, quando você atribui um valor a uma variável, o valor não é passado para o pipeline ou console. Quando você faz uma atribuição de variável em uma subexpressão, ela é passada para o pipeline.

PS> $first = 1
PS> ($second = 2)
2

Confira como a atribuição de $first não tem saída e a atribuição de $second tem. Quando uma atribuição é feita em uma instrução if, ela é executada da mesma forma que a atribuição de $second acima. Aqui está um exemplo limpo sobre como você poderia usá-lo:

if ( $process = Get-Process Notepad* )
{
    $process | Stop-Process
}

Se $process receber um valor, a instrução será $true e $process será interrompida.

Certifique-se de não confundir isso com -eq porque essa não é uma verificação de igualdade. Esse é um recurso mais obscuro que a maioria das pessoas não percebe que funciona assim.

Atribuição de variável do bloco de script

Você também pode usar o bloco de script de instrução if para atribuir um valor a uma variável.

$discount = if ( $age -ge 55 )
{
    Get-SeniorDiscount
}
elseif ( $age -le 13 )
{
    Get-ChildDiscount
}
else
{
    0.00
}

Cada bloco de script está gravando os resultados dos comandos ou o valor como saída. Podemos atribuir o resultado da instrução if à variável $discount. Esse exemplo poderia ter atribuído facilmente esses valores à variável $discount diretamente em cada scriptblock. Não posso dizer que uso isso com a instrução if com frequência, mas tenho um exemplo em que usei isso recentemente.

Caminho de execução alternativo

A instrução if permite que você especifique uma ação não apenas para quando a instrução for $true, mas também para quando for $false. É aí que a instrução else entra em cena.

else

A instrução else é sempre a última parte da instrução if quando usada.

if ( Test-Path -Path $Path -PathType Leaf )
{
    Move-Item -Path $Path -Destination $archivePath
}
else
{
    Write-Warning "$path doesn't exist or isn't a file."
}

Neste exemplo, verificamos a $path para verificar se é um arquivo. Se encontrarmos o arquivo, o moveremos. Caso contrário, escreveremos um aviso. Esse tipo de lógica de ramificação é muito comum.

if aninhado

As instruções if e else tomam um bloco de script, para que possamos colocar qualquer comando do PowerShell dentro delas, incluindo outra instrução if. Isso permite que você use uma lógica muito mais complicada.

if ( Test-Path -Path $Path -PathType Leaf )
{
    Move-Item -Path $Path -Destination $archivePath
}
else
{
    if ( Test-Path -Path $Path )
    {
        Write-Warning "A file was required but a directory was found instead."
    }
    else
    {
        Write-Warning "$path could not be found."
    }
}

Neste exemplo, testamos o caminho feliz primeiro e, em seguida, tomamos medidas sobre ele. Se isso falhar, faremos outra verificação e forneceremos informações mais detalhadas ao usuário.

elseif

Não estamos limitados a apenas uma única verificação condicional. Podemos encadear instruções if e else juntas em vez de aninhar usando a instrução elseif.

if ( Test-Path -Path $Path -PathType Leaf )
{
    Move-Item -Path $Path -Destination $archivePath
}
elseif ( Test-Path -Path $Path )
{
    Write-Warning "A file was required but a directory was found instead."
}
else
{
    Write-Warning "$path could not be found."
}

A execução ocorre de cima para baixo. A instrução if superior é avaliada primeiro. Se for $false, passará para a próxima elseif ou else na lista. Essa última else é a ação padrão a ser tomada se nenhuma das outras retornar $true.

switch

Neste ponto, preciso mencionar a declaração switch. Ele fornece uma sintaxe alternativa para fazer várias comparações com um valor. Com o switch, você especifica uma expressão e esse resultado é comparado com vários valores diferentes. Se um desses valores corresponder, o bloco de código correspondente será executado. Dê uma olhada neste exemplo:

$itemType = 'Role'
switch ( $itemType )
{
    'Component'
    {
        'is a component'
    }
    'Role'
    {
        'is a role'
    }
    'Location'
    {
        'is a location'
    }
}

Há três valores possíveis que podem corresponder ao $itemType. Nesse caso, ele corresponde ao Role. Eu usei um exemplo simples apenas para lhe dar um pouco de familiaridade com o operador switch. Falarei mais sobre tudo o que você queria saber sobre a instrução switch em outro artigo.

Matriz embutida

Tenho uma função chamada Invoke-SnowSql que inicia um executável com vários argumentos de linha de comando. Aqui está um clipe dessa função em que eu compilo a matriz de argumentos.

$snowSqlParam = @(
    '--accountname', $Endpoint
    '--username', $Credential.UserName
    '--option', 'exit_on_error=true'
    '--option', 'output_format=csv'
    '--option', 'friendly=false'
    '--option', 'timing=false'
    if ($Debug)
    {
        '--option', 'log_level=DEBUG'
    }
    if ($Path)
    {
        '--filename', $Path
    }
    else
    {
        '--query', $singleLineQuery
    }
)

As variáveis $Debug e $Path são parâmetros na função fornecida pelo usuário final. Posso avaliar embutidos dentro da inicialização da minha matriz. Se $Debug for verdadeiro, esses valores serão inseridos no $snowSqlParam no lugar correto. O mesmo vale para a variável $Path.

Simplificar operações complexas

É inevitável enfrentar situações em que haja muitas comparações a serem verificadas e a instrução if fique na extremidade direita da tela.

$user = Get-ADUser -Identity $UserName
if ( $null -ne $user -and $user.Department -eq 'Finance' -and $user.Title -match 'Senior' -and $user.HomeDrive -notlike '\\server\*' )
{
    # Do Something
}

Eles podem ser difíceis de ler e isso te tornam mais propenso a cometer erros. Há algumas coisas que podemos fazer sobre isso.

Continuação de linha

Há alguns operadores no PowerShell que permitem encapsular o comando para a próxima linha. Os operadores lógicos -and e -or são bons operadores a serem usados se você quiser dividir sua expressão em várias linhas.

if ($null -ne $user -and
    $user.Department -eq 'Finance' -and
    $user.Title -match 'Senior' -and
    $user.HomeDrive -notlike '\\server\*'
)
{
    # Do Something
}

Ainda há muita coisa acontecendo, mas colocar cada peça em sua própria linha faz uma grande diferença. Geralmente uso isso quando recebo mais de duas comparações ou se tenho que rolar para a direita para ler qualquer uma das lógicas.

Resultados de pré-cálculo

Podemos retirar essa declaração da instrução if e verificar apenas o resultado.

$needsSecureHomeDrive = $null -ne $user -and
    $user.Department -eq 'Finance' -and
    $user.Title -match 'Senior' -and
    $user.HomeDrive -notlike '\\server\*'

if ( $needsSecureHomeDrive )
{
    # Do Something
}

Isso parece muito mais limpo do que o exemplo anterior. Você também tem a oportunidade de usar um nome de variável que explica o que é que você está realmente verificando. Este também é um exemplo de código de auto-documentação que salva comentários desnecessários.

Várias instruções if

Podemos dividir isso em várias afirmações e verificá-las uma de cada vez. Nesse caso, usamos um sinalizador ou uma variável de acompanhamento para combinar os resultados.


$skipUser = $false

if( $null -eq $user )
{
    $skipUser = $true
}

if( $user.Department -ne 'Finance' )
{
    Write-Verbose "isn't in Finance department"
    $skipUser = $true
}

if( $user.Title -match 'Senior' )
{
    Write-Verbose "Doesn't have Senior title"
    $skipUser = $true
}

if( $user.HomeDrive -like '\\server\*' )
{
    Write-Verbose "Home drive already configured"
    $skipUser = $true
}

if ( -not $skipUser )
{
    # do something
}

Eu tive que inverter a lógica para fazer a lógica de bandeira funcionar corretamente. Cada avaliação é uma instrução if individual. A vantagem disso é que, quando você estiver depurando, poderá dizer exatamente o que a lógica está fazendo. Ao mesmo tempo, consegui adicionar um detalhamento muito melhor.

A desvantagem óbvia é que é muito mais código para escrever. O código é mais complexo de examinar, pois ele usa uma única linha de lógica e o explode em 25 ou mais linhas.

Usar funções

Também podemos mover toda essa lógica de validação para uma função. Veja como isso parece limpo à primeira vista.

if ( Test-SecureDriveConfiguration -ADUser $user )
{
    # do something
}

Você ainda precisa criar a função para fazer a validação, mas isso torna esse código muito mais fácil de trabalhar. Isso facilita o teste desse código. Nos seus testes, você pode simular a chamada para Test-ADDriveConfiguration e precisa de apenas dois testes para essa função. Um em que retorna $true e outro em que retorna $false. Testar a outra função é mais simples porque ela é tão pequena.

O corpo dessa função ainda pode ser aquela linha única com a qual começamos ou a lógica expandida que usamos na última seção. Isso funciona bem para ambos os cenários e permite que você altere facilmente essa implementação mais tarde.

Tratamento de erros

Um uso importante da instrução if é verificar se há condições de erro antes de ocorrerem erros. Um bom exemplo é verificar se uma pasta já existe antes de você tentar criá-la.

if ( -not (Test-Path -Path $folder) )
{
    New-Item -Type Directory -Path $folder
}

Eu gosto de dizer que se você espera que uma exceção aconteça, então não é realmente uma exceção. Portanto, verifique seus valores e valide suas condições onde puder.

Se você quiser se aprofundar um pouco mais no manejo de exceções de forma prática, tenho um artigo sobre tudo o que você sempre quis saber a respeito de exceções.

Palavras finais

A instrução if é uma instrução simples, mas é uma parte fundamental do PowerShell. Você se verá usando isso várias vezes em quase todos os scripts que você escreve. Espero que tenha uma compreensão melhor do que tinha antes.