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

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

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.

Execução condicional

Seus scripts geralmente precisam tomar decisões e executar lógicas diferentes com base nessas decisões. É isto que quero dizer com execução condicional. Você tem uma instrução ou valor para avaliar e, em seguida, executar uma seção diferente de código com base nessa avaliação. É exatamente isso que a if declaração faz.

A if declaração

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

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

A primeira coisa que o enunciado if faz é avaliar a expressão entre parênteses. Se ele avalia para $true, então ele executa o scriptblock nos chaves. Se o valor fosse $false, ele ignoraria esse scriptblock.

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

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

Operadores de comparação

O uso mais comum da if declaração para é 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 com o valor no lado direito.

-QE para a igualdade

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

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

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

Um caso de uso possível é verificar o status de um valor antes de executar uma ação sobre ele. Você pode obter um serviço e verificar se o status estava em execução antes de chamá-lo 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 (ex: 5 = $value) que é reservado para atribuir valores a variáveis. Ao colocar o seu valor conhecido à esquerda, torna esse erro mais difícil de cometer.

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

  • -eq Igualdade insensível a maiúsculas e minúsculas
  • -ieq Igualdade insensível a maiúsculas e minúsculas
  • -ceq igualdade sensível a maiúsculas e minúsculas

-ne não igual

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

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

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

Variações:

  • -ne insensível a maiúsculas e minúsculas não igual
  • -ine insensível a maiúsculas e minúsculas não igual
  • -cne sensível a maiúsculas e minúsculas não igual

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

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

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

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

Variações:

  • -gt superior a
  • -igt maior que, sem diferenciação de maiúsculas e minúsculas
  • -cgt maior que, sensível a maiúsculas e minúsculas
  • -ge maior ou igual
  • -ige maior ou igual, sem diferenciação de maiúsculas e minúsculas
  • -cge maior ou igual, sensível a maiúsculas e minúsculas
  • -lt menos de
  • -ilt menos do que, sem distinção entre maiúsculas e minúsculas
  • -clt menos do que, sensível a maiúsculas e minúsculas
  • -le menor ou igual
  • -ile menor ou igual, sem diferenciação de maiúsculas e minúsculas
  • -cle menor ou igual, sensível a maiúsculas e minúsculas

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

-como partidas curinga

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

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

É importante salientar que o padrão corresponde a toda a string. Se você precisar combinar algo no meio da string, você precisa colocar o * em ambas as extremidades da string.

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

Variações:

  • -like Curinga que não diferencia maiúsculas de minúsculas
  • -ilike Curinga que não diferencia maiúsculas de minúsculas
  • -clike Curinga que diferencia maiúsculas de minúsculas
  • -notlike Curinga que não diferencia maiúsculas de minúsculas não correspondido
  • -inotlike Curinga que não diferencia maiúsculas de minúsculas não correspondido
  • -cnotlike Curinga que diferencia maiúsculas de minúsculas não correspondido

-corresponder expressão regular

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

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

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

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

Regex é uma linguagem complexa por si só e vale a pena investigar. Eu falo mais sobre -match e as muitas maneiras de usar regex em outro artigo.

Variações:

  • -match regex que não diferencia maiúsculas de minúsculas
  • -imatch regex que não diferencia maiúsculas de minúsculas
  • -cmatch regex sensível a maiúsculas e minúsculas
  • -notmatch regex que não diferencia maiúsculas de minúsculas não correspondido
  • -inotmatch regex que não diferencia maiúsculas de minúsculas não correspondido
  • -cnotmatch regex sensível a maiúsculas e minúsculas não correspondido

-é do tipo

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

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

Você pode usar isso se estiver trabalhando com classes ou aceitando vários objetos ao longo do pipeline. Você pode ter um serviço ou um nome de serviço como sua entrada. Em seguida, verifique se você tem um serviço e busque o serviço se você tiver apenas o nome.

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

Variações:

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

Operadores de recolha

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 cada valor avaliado para $true.

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

Isso ainda funciona corretamente em uma if declaração. Assim, um valor é devolvido pelo seu operador, então toda a instrução é $true.

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

Há uma pequena armadilha escondida nos detalhes aqui que eu preciso apontar. Ao usar o -ne operador dessa forma, é fácil olhar erroneamente para a lógica de trás para frente. Usar -ne com uma coleção retorna $true se algum item da 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-in que lidam com isso de forma mais eficiente. E -notcontains faz o que espera.

-contém

O -contains operador verifica a cobrança quanto ao seu valor. Assim que encontrar uma correspondência, ele retorna $true.

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

Esta é a maneira preferida de ver se uma coleção contém o seu valor. Usar Where-Object (ou -eq) percorre toda a lista todas as vezes e é significativamente mais lento.

Variações:

  • -contains correspondência que não diferencia maiúsculas de minúsculas
  • -icontains correspondência que não diferencia maiúsculas de minúsculas
  • -ccontains correspondência que diferencia maiúsculas de minúscul
  • -notcontains insensível a maiúsculas e minúsculas não correspondido
  • -inotcontains insensível a maiúsculas e minúsculas não correspondido
  • -cnotcontains sensível a maiúsculas e minúsculas não correspondido

-em

O -in operador é como o operador, -contains exceto que a coleta está no lado direito.

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

Variações:

  • -in correspondência que não diferencia maiúsculas de minúsculas
  • -iin correspondência que não diferencia maiúsculas de minúsculas
  • -cin correspondência que diferencia maiúsculas de minúscul
  • -notin insensível a maiúsculas e minúsculas não correspondido
  • -inotin insensível a maiúsculas e minúsculas não correspondido
  • -cnotin sensível a maiúsculas e minúsculas não correspondido

Operadores lógicos

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

-não

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

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

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

! operador

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

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

Você pode ver ! mais usado por pessoas que vêm de outras linguagens como C#. Eu prefiro digitá-lo porque acho difícil de ver quando olho rapidamente para meus scripts.

-e

Você pode combinar expressões com o -and operador. 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 acontece da esquerda para a direita. Se o primeiro item for avaliado como $false, ele sai cedo e não realiza a comparação correta. Isso é útil quando você precisa se certificar de que um valor existe antes de usá-lo. Por exemplo, Test-Path lança um erro se você lhe der um $null caminho.

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

-ou

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

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

Tal como acontece com o -and operador, a avaliação acontece da esquerda para a direita. Só que se a primeira parte é $true, então toda a instrução é $true e não processa o resto da expressão.

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

-xor exclusivo ou

Este é um pouco incomum. -xor permite que apenas uma expressão seja avaliada a $true. Portanto, se ambos os itens são $false ou ambos os itens são $true, então toda a expressão é $false. Outra maneira de olhar para isso é a expressão é apenas $true quando os resultados da expressão são diferentes.

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

Operadores bit-a-bit

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

  • -band binário E
  • -bor binário OU
  • -bxor binário exclusivo OU
  • -bnot binário NÃO
  • -shl Vire para a esquerda
  • -shr Vire para a direita

Expressões do PowerShell

Podemos usar o PowerShell normal dentro da instrução de condição.

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* )

Ele avalia se $true há um processo devolvido e $false se não há. É 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 -and operadores e -or , mas você pode ter que usar parênteses para dividi-las em subexpressões.

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

Verificando se há $null

Ter um resultado ou um $null valor não é $false avaliado na if declaração. Ao verificar especificamente o $null, é uma prática recomendada colocar o $null no lado esquerdo.

if ( $null -eq $value )

Há algumas nuances ao lidar com $null valores 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 variável dentro da condição

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

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

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

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

Se $process for atribuído um valor, então a instrução é $true e $process é interrompida.

Certifique-se de não confundir isso com -eq porque isso não é uma verificação de igualdade. Esta é uma característica mais obscura que a maioria das pessoas não percebe que funciona desta forma.

Atribuição variável do scriptblock

Você também pode usar a if instrução scriptblock 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 if instrução à $discount variável. Esse exemplo poderia ter facilmente atribuído esses valores à $discount variável diretamente em cada bloco de script. Não posso dizer que uso isso com a declaração com frequência if , mas tenho um exemplo em que usei isso recentemente.

Caminho de execução alternativo

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

else

A else instrução é sempre a última parte da if declaração 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 se é $path um arquivo. Se encontrarmos o ficheiro, movemo-lo. Se não, escrevemos um aviso. Este tipo de lógica de ramificação é muito comum.

Aninhado se

As if instruções e else usam um bloco de script, para que possamos colocar qualquer comando do PowerShell dentro delas, incluindo outra if instrução. Isso permite que você faça uso de 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 primeiro o caminho feliz e, em seguida, agimos sobre ele. Se isso falhar, fazemos outra verificação e fornecemos informações mais detalhadas ao usuário.

elseif

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

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 acontece de cima para baixo. A declaração superior if é avaliada primeiro. Se for $false, então ele passa para o próximo 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 switch declaração. 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. Veja este 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. Neste caso, corresponde ao Role. Eu usei um exemplo simples apenas para lhe dar alguma exposição ao switch operador. Eu falo mais sobre tudo o que você sempre quis saber sobre a declaração de mudança em outro artigo.

Array em linha

Eu 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 onde eu construo 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 $Debug variáveis e $Path são parâmetros na função que são fornecidos pelo usuário final. Eu os avalio em linha dentro da inicialização da minha matriz. Se $Debug for verdade, então esses valores caem $snowSqlParam no lugar correto. O mesmo vale para a $Path variável.

Simplifique operações complexas

É inevitável que você se depare com uma situação que tem muitas comparações para verificar e sua If declaração rola para fora do lado direito 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 que o tornam mais propenso a cometer erros. Há algumas coisas que podemos fazer a esse respeito.

Continuação da linha

Há alguns operadores no PowerShell que permitem que você envolva seu comando para a próxima linha. Os operadores -and lógicos e -or são bons operadores para usar 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 por lá, mas colocar cada peça em sua própria linha faz uma grande diferença. Eu 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.

Pré-cálculo dos resultados

Podemos retirar essa declaração da if declaração e apenas verificar 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 você está realmente verificando. Este também é um exemplo de código de auto-documentação que salva comentários desnecessários.

Múltiplas instruções if

Podemos dividir isso em várias declarações e verificá-las uma de cada vez. Neste caso, usamos um sinalizador ou uma variável de rastreamento 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 da bandeira funcionar corretamente. Cada avaliação é uma declaração individual if . A vantagem disso é que, quando você está depurando, você pode dizer exatamente o que a lógica está fazendo. Eu fui capaz de adicionar muito melhor verbosidade ao mesmo tempo.

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

Usando funções

Também podemos mover toda essa lógica de validação para uma função. Olhe para o quão limpo isso parece em um piscar de olhos.

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 torna esse código mais fácil de testar. Em seus testes, você pode simular a chamada para Test-ADDriveConfiguration e você só precisa de dois testes para esta função. Um onde regressa $true e outro onde regressa $false. Testar a outra função é mais simples porque é muito pequena.

O corpo dessa função ainda pode ser aquele one-liner com o qual começamos ou a lógica explodida 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.

Processamento de erros

Um uso importante da instrução é verificar se if há condições de erro antes de encontrar erros. Um bom exemplo é verificar se uma pasta já existe antes de 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. Por isso, verifique os seus valores e valide as suas condições sempre que possível.

Se você quiser mergulhar um pouco mais no tratamento real de exceções, tenho um artigo sobre tudo o que você sempre quis saber sobre exceções.

Palavras finais

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