about_Pipeline_Chain_Operators

Descrição curta

Descreve pipelines de encadeamento com os operadores && e || no PowerShell.

Descrição longa

A partir do PowerShell 7, o PowerShell implementa os operadores && e || para encadear pipelines condicionalmente. Esses operadores são conhecidos no PowerShell como operadores de cadeia de pipeline e são semelhantes a listas de AND-OR em shells POSIX, como bash, zsh e sh, bem como símbolos de processamento condicional no Shell de Comando do Windows (cmd.exe).

O operador && executa o pipeline à direita, se o pipeline à esquerda tiver sido bem-sucedido. Por outro lado, o operador || executará o pipeline à direita se o pipeline esquerdo falhar.

Esses operadores usam as variáveis $? e $LASTEXITCODE para determinar se um pipeline falhou. Isso permite que você os use com comandos nativos e não apenas com cmdlets ou funções. Por exemplo:

# Create an SSH key pair - if successful copy the public key to clipboard
ssh-keygen -t rsa -b 2048 && Get-Content -Raw ~\.ssh\id_rsa.pub | clip

Exemplos

Dois comandos bem-sucedidos

Write-Output 'First' && Write-Output 'Second'
First
Second

O primeiro comando falha, fazendo com que o segundo não seja executado

Write-Error 'Bad' && Write-Output 'Second'
Write-Error: Bad

O primeiro comando é bem-sucedido, portanto, o segundo comando não é executado

Write-Output 'First' || Write-Output 'Second'
First

O primeiro comando falha e, portanto, o segundo comando é executado

Write-Error 'Bad' || Write-Output 'Second'
Write-Error: Bad
Second

O sucesso do pipeline é definido pelo valor da variável $?, que o PowerShell define automaticamente após a execução de um pipeline com base em seu status de execução. Isso significa que os operadores de cadeia de pipeline têm a seguinte equivalência:

Test-Command '1' && Test-Command '2'

funciona da mesma forma que

Test-Command '1'; if ($?) { Test-Command '2' }

e

Test-Command '1' || Test-Command '2'

funciona da mesma forma que

Test-Command '1'; if (-not $?) { Test-Command '2' }

Atribuição de cadeias de pipeline

Atribuir uma variável de uma cadeia de pipeline usa a concatenação de todos os pipelines na cadeia:

$result = Write-Output '1' && Write-Output '2'
$result
1
2

Se ocorrer um erro de encerramento de script durante a atribuição de uma cadeia de pipeline, a atribuição não terá êxito:

try
{
    $result = Write-Output 'Value' && $(throw 'Bad')
}
catch
{
    # Do nothing, just squash the error
}

"Result: $result"
Result:

Sintaxe e precedência do operador

Ao contrário de outros operadores, && e || operam em pipelines, em vez de em expressões como + ou -and, por exemplo.

&& e || têm uma precedência inferior à de tubulação (|) ou redirecionamento (>), mas uma precedência maior que operadores de trabalho (&), atribuição (=) ou ponto-e-vírgula (;). Isso significa que os pipelines em uma cadeia de pipeline podem ser redirecionados individualmente e que cadeias de pipeline inteiras podem ser em segundo plano, atribuídas a variáveis ou separadas como instruções.

Para usar a sintaxe de precedência inferior em uma cadeia de pipeline, considere o uso de parênteses (...). Da mesma forma, para inserir uma instrução em uma cadeia de pipeline, uma subexpressão $(...) pode ser usada. Isso pode ser útil para combinar comandos nativos com o fluxo de controle:

foreach ($file in 'file1','file2','file3')
{
    # When find succeeds, the loop breaks
    find $file && Write-Output "Found $file" && $(break)
}
find: file1: No such file or directory
file2
Found file2

A partir do PowerShell 7, o comportamento dessas sintaxes foi alterado para que $? seja definido como esperado quando um comando for bem-sucedido ou falhar entre parênteses ou uma subexpressão.

Como a maioria dos outros operadores no PowerShell, && e || também são associativos à esquerda, o que significa que eles agrupam da esquerda. Por exemplo:

Get-ChildItem -Path ./file.txt ||
    Write-Error "file.txt doesn't exist" &&
    Get-Content -Raw ./file.txt

agrupará como:

(Get-ChildItem -Path ./file.txt || Write-Error "file.txt doesn't exist") &&
    Get-Content -Raw ./file.txt

sendo equivalente a:

Get-ChildItem -Path ./file.txt

if (-not $?) { Write-Error "file.txt does not exist" }

if ($?) { Get-Content -Raw ./file.txt }

Interação de erro

Os operadores de cadeia de pipeline não absorvem erros. Quando uma instrução em uma cadeia de pipeline gera um erro de encerramento de script, a cadeia de pipeline é encerrada.

Por exemplo:

$(throw 'Bad') || Write-Output '2'
Exception: Bad

Mesmo quando o erro é capturado, a cadeia de pipeline ainda é encerrada:

try
{
    $(throw 'Bad') || Write-Output '2'
}
catch
{
    Write-Output "Caught: $_"
}
Write-Output 'Done'
Caught: Bad
Done

Se um erro não for encerrado ou terminar apenas um pipeline, a cadeia de pipeline continuará, respeitando o valor de $?:

function Test-NonTerminatingError
{
    [CmdletBinding()]
    param()

    $exception = [System.Exception]::new('BAD')
    $errorId = 'BAD'
    $errorCategory = 'NotSpecified'

    $errorRecord = [System.Management.Automation.ErrorRecord]::new(
        $exception, $errorId, $errorCategory, $null
    )

    $PSCmdlet.WriteError($errorRecord)
}

Test-NonTerminatingError || Write-Output 'Second'
Test-NonTerminatingError: BAD
Second

Encadeamento de pipelines em vez de comandos

Os operadores de cadeia de pipeline, por seu nome, podem ser usados para encadear pipelines, em vez de apenas comandos. Isso corresponde ao comportamento de outros shells, mas pode tornar o sucesso mais difícil de determinar:

function Test-NotTwo
{
    [CmdletBinding()]
    param(
      [Parameter(ValueFromPipeline)]
      $Input
    )

    process
    {
        if ($Input -ne 2)
        {
            return $Input
        }

        $exception = [System.Exception]::new('Input is 2')
        $errorId = 'InputTwo'
        $errorCategory = 'InvalidData'

        $errorRecord = [System.Management.Automation.ErrorRecord]::new(
            $exception, $errorId, $errorCategory, $null
        )

        $PSCmdlet.WriteError($errorRecord)
    }
}

1,2,3 | Test-NotTwo && Write-Output 'All done!'
1
Test-NotTwo : Input is 2
3

Observe que Write-Output 'All done!' não é executado, uma vez que Test-NotTwo é considerado com falha após gerar o erro de não terminação.

Consulte também