Condividi tramite


about_Pipeline_Chain_Operators

Breve descrizione

Descrive le pipeline di concatenamento con gli && operatori e || in PowerShell.

Descrizione lunga

A partire da PowerShell 7, PowerShell implementa gli && operatori e || per concatenare in modo condizionale le pipeline. Questi operatori sono noti in PowerShell come operatori della catena di pipeline e sono simili agli elenchi AND-OR nelle shell POSIX come bash, zsh e sh, nonché simboli di elaborazione condizionale in Windows Command Shell (cmd.exe).

L'operatore && esegue la pipeline di destra se la pipeline di sinistra ha avuto esito positivo. Viceversa, l'operatore || esegue la pipeline di destra se la pipeline di sinistra ha avuto esito negativo.

Questi operatori usano le variabili $? e $LASTEXITCODE per determinare se una pipeline ha avuto esito negativo. Ciò consente di usarli con comandi nativi e non solo con cmdlet o funzioni. Ad esempio:

# 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

Esempi

Due comandi riusciti

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

Il primo comando ha esito negativo, causando l'esecuzione della seconda non

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

Il primo comando ha esito positivo, quindi il secondo comando non viene eseguito

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

Il primo comando ha esito negativo, quindi il secondo comando viene eseguito

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

L'esito positivo della pipeline è definito dal valore della variabile, che PowerShell imposta automaticamente dopo l'esecuzione $? di una pipeline in base allo stato di esecuzione. Ciò significa che gli operatori della catena di pipeline hanno l'equivalenza seguente:

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

funziona come

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

e

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

funziona come

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

Assegnazione da catene di pipeline

L'assegnazione di una variabile da una catena di pipeline accetta la concatenazione di tutte le pipeline della catena:

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

Se si verifica un errore di terminazione dello script durante l'assegnazione da una catena di pipeline, l'assegnazione non riesce:

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

"Result: $result"
Result:

Sintassi dell'operatore e precedenza

A differenza di altri operatori, && e || operano su pipeline, anziché su espressioni come + o -and, ad esempio.

&&e || avere una precedenza inferiore rispetto a piping> () o reindirizzamento (), ma una precedenza maggiore rispetto agli operatori di processo (&), assegnazione (|=) o punti e virgola (;). Ciò significa che le pipeline all'interno di una catena di pipeline possono essere reindirizzate singolarmente e che l'intera catena di pipeline può essere in background, assegnata a variabili o separate come istruzioni.

Per usare la sintassi di precedenza inferiore all'interno di una catena di pipeline, prendere in considerazione l'uso delle parentesi (...). Analogamente, per incorporare un'istruzione all'interno di una catena di pipeline, è possibile usare una sottoespressione $(...) . Ciò può risultare utile per combinare comandi nativi con il flusso di controllo:

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 partire da PowerShell 7, il comportamento di queste sintassi è stato modificato in modo che $? sia impostato come previsto quando un comando riesce o non riesce tra parentesi o sottoespressione.

Come la maggior parte degli altri operatori in PowerShell, && e || sono anche associativi a sinistra, ovvero raggruppano da sinistra. Ad esempio:

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

raggruppa come:

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

equivalente a:

Get-ChildItem -Path ./file.txt

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

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

Interazione degli errori

Gli operatori della catena di pipeline non assorbono errori. Quando un'istruzione in una catena di pipeline genera un errore di terminazione dello script, la catena di pipeline viene terminata.

Ad esempio:

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

Anche quando viene rilevato l'errore, la catena di pipeline viene ancora terminata:

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

Se un errore non termina o termina solo una pipeline, la catena di pipeline continua, rispettando il valore di $?:

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

Pipeline di concatenamento anziché comandi

Gli operatori della catena di pipeline, in base al nome, possono essere usati per concatenare le pipeline, anziché solo i comandi. Ciò corrisponde al comportamento di altre shell, ma può rendere più difficile determinare:

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

Si noti che Write-Output 'All done!' non viene eseguito, poiché Test-NotTwo viene considerato non riuscito dopo aver generato l'errore non terminante.

Vedi anche