about_Pipeline_Chain_Operators

Descrizione breve

Descrive il concatenamento delle pipeline 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 nella shell dei comandi di Windows (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 la mancata esecuzione del secondo

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 viene eseguito il secondo comando

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 allo stesso modo di

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

e

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

funziona allo stesso modo di

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 nella 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 e precedenza degli operatori

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

&&e || hanno una precedenza inferiore rispetto al piping () o al reindirizzamento (|>), ma una precedenza maggiore rispetto agli operatori di processo (), l'assegnazione (&=) o il punto 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, considerare l'uso delle parentesi (...). Analogamente, per incorporare un'istruzione all'interno di una catena di pipeline, è possibile usare una sottoespressione $(...) . Ciò può essere 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 $? venga 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 associati 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

verrà raggruppato come:

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

equivale a:

Get-ChildItem -Path ./file.txt

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

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

Interazione con errori

Gli operatori della catena di pipeline non assorbono gli 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

Concatenamento di pipeline anziché comandi

Gli operatori della catena di pipeline, in base al nome, possono essere usati per concatenare le pipeline, anziché solo i comandi. Questo 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 la generazione dell'errore non irreversibile.

Vedi anche