about_Pipeline_Chain_Operators

Kurze Beschreibung

Beschreibt die Verkettung von Pipelines mit den && Operatoren || in PowerShell.

Lange Beschreibung

Ab PowerShell 7 implementiert PowerShell die und || die && Operatoren in bedingten Verketten von Pipelines. Diese Operatoren sind in PowerShell als Pipelinekettenoperatoren bekannt und ähneln AND-OR-Listen in POSIX-Shells wie Bash, zsh und sh sowie bedingte Verarbeitungssymbole in der Windows-Befehlsshell (cmd.exe).

Der Operator && führt die rechte Pipeline aus, wenn die linke Pipeline erfolgreich war. Dagegen führt der Operator || die rechte Pipeline aus, wenn die linke Pipeline fehlgeschlagen ist.

Diese Operatoren verwenden die Variablen $? und $LASTEXITCODE, um zu bestimmen, ob eine Pipeline fehlgeschlagen ist. Dadurch können Sie sie mit nativen Befehlen und nicht bloß mit Cmdlets oder Funktionen verwenden. Zum Beispiel:

# 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

Beispiele

Zwei erfolgreiche Befehle

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

Der erste Befehl schlägt fehl, was zur Folge hat, dass der zweite Befehl nicht ausgeführt wird.

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

Der erste Befehl ist erfolgreich, sodass der zweite Befehl nicht ausgeführt wird.

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

Der erste Befehl schlägt fehl, sodass der zweite Befehl ausgeführt wird.

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

Der Pipelineerfolg wird durch den Wert der $? Variablen definiert, den PowerShell automatisch nach dem Ausführen einer Pipeline basierend auf dem Ausführungsstatus festlegt. Dies bedeutet, dass Pipelinekettenbetreiber die folgende Äquivalenz haben:

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

funktioniert genauso wie

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

and

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

funktioniert genauso wie

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

Zuordnung aus Pipelineketten

Durch das Zuweisen einer Variablen aus einer Pipelinekette wird die Verkettung aller Pipelines in der Kette verwendet:

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

Wenn während der Zuweisung aus einer Pipelinekette ein Skriptendefehler auftritt, ist die Zuordnung nicht erfolgreich:

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

"Result: $result"
Result:

Operatorsyntax und Rangfolge

Im Gegensatz zu anderen Operatoren && und || arbeiten Sie nicht auf Ausdrücken wie oder , sondern auf Pipelines wie + oder -andbeispielsweise.

&&und || haben eine niedrigere Rangfolge als Piping (|) oder Umleitung (>), aber eine höhere Rangfolge als Auftragsoperatoren (&), Zuordnung () oder Semikolons (=;). Dies bedeutet, dass Pipelines innerhalb einer Pipelinekette einzeln umgeleitet werden können und dass ganze Pipelineketten hintergrundgebunden, Variablen zugewiesen oder als Anweisungen getrennt werden können.

Wenn Sie eine niedrigere Rangfolgensyntax in einer Pipelinekette verwenden möchten, sollten Sie die Verwendung von Klammern (...)in Betracht ziehen. Ebenso kann ein Unterausdruck $(...) verwendet werden, um eine Anweisung in eine Pipelinekette einzubetten. Dies kann nützlich sein, um systemeigene Befehle mit Steuerungsfluss zu kombinieren:

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

Ab PowerShell 7 wurde das Verhalten dieser Syntaxen so geändert, dass sie $? wie erwartet festgelegt wird, wenn ein Befehl erfolgreich ist oder innerhalb von Klammern oder unter einem Unterausdruck fehlschlägt.

Wie die meisten anderen Operatoren in PowerShell && sind sie || auch linksassoziativ, d. h. sie gruppieren von links. Zum Beispiel:

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

gruppieren als:

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

entspricht:

Get-ChildItem -Path ./file.txt

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

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

Fehlerinteraktion

Pipelinekettenoperatoren absorbieren keine Fehler. Wenn eine Anweisung in einer Pipelinekette einen Skriptbeendfehler auslöst, wird die Pipelinekette beendet.

Zum Beispiel:

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

Auch wenn der Fehler abgefangen wird, wird die Pipelinekette noch beendet:

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

Wenn ein Fehler nicht beendet wird oder nur eine Pipeline beendet wird, wird die Pipelinekette fortgesetzt, wobei der Wert von $?:

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

Verketten von Pipelines anstelle von Befehlen

Pipelinekettenoperatoren können anhand ihres Namens anstelle von Befehlen verkettet werden. Dies entspricht dem Verhalten anderer Shells, kann jedoch die Ermittlung von Erfolg erschweren:This matches the behavior of other shells, but can make success harder to determine:

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

Beachten Sie, dass Write-Output 'All done!' sie nicht ausgeführt wird, da Test-NotTwo nach dem Generieren des nicht beendeten Fehlers als fehlgeschlagen gilt.

Weitere Informationen