Freigeben über


about_Pipeline_Chain_Operators

Kurzbeschreibung

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

Lange Beschreibung

Ab PowerShell 7 implementiert PowerShell das && und || Operatoren zur bedingten Verkettung von Pipelines. Diese Operatoren sind in PowerShell bekannt als Pipeline-Kettenbetreiber, und sind vergleichbar mit AND-OR-Listen in POSIX-Shells wie bash, zsh und sh, sowie in Symbole für die bedingte Verarbeitung in der Windows Command Shell (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 ermitteln, ob eine Pipeline fehlgeschlagen ist. Auf diese Weise können Sie sie mit nativen Befehlen und nicht nur 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, die PowerShell nach der Ausführung einer Pipeline basierend auf ihrem Ausführungsstatus automatisch 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' }

und

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

funktioniert genauso wie

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

Zuordnung von Pipeline-Ketten

Für die Zuweisung einer Variablen aus einer Pipeline-Kette ist die Verkettung aller Pipelines in der Kette erforderlich:

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

Wenn während der Zuweisung aus einer Pipeline-Kette ein Skript-Beendigungsfehler auftritt, ist die Zuweisung nicht erfolgreich:

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

"Result: $result"
Result:

Syntax und Vorrang von Operatoren

Im Gegensatz zu anderen Betreibern, && und || auf Pipelines wirken und nicht auf Ausdrücke wie + oder -and, zum Beispiel.

&& und || haben einen geringeren Vorrang als Rohrleitungen (|) oder Umleitung (>), aber einen höheren Vorrang als Job-Operatoren (&), Auftrag (=) oder Semikolons (;). Das bedeutet, dass Pipelines innerhalb einer Pipeline-Kette einzeln umgeleitet werden können und dass ganze Pipeline-Ketten in den Hintergrund gestellt, Variablen zugewiesen oder als Anweisungen getrennt werden können.

Um eine Syntax mit niedrigerem Vorrang innerhalb einer Pipelinekette zu verwenden, sollten Sie Klammern verwenden (...). Um eine Anweisung in eine Pipeline-Kette einzubetten, muss ein Unterausdruck $(...) verwendet werden können. Dies kann nützlich sein, um native Befehle mit Kontrollfluss 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 dahingehend geändert, dass $? wird wie erwartet gesetzt, wenn ein Befehl innerhalb von Klammern oder einem Unterausdruck erfolgreich ist oder fehlschlägt.

Wie die meisten anderen Operatoren in PowerShell, && und || sind auch links-assoziativ, das heißt, sie gruppieren sich 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

gleichwertig zu sein:

Get-ChildItem -Path ./file.txt

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

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

Fehler-Interaktion

Die Betreiber von Pipeline-Ketten absorbieren keine Fehler. Wenn eine Anweisung in einer Pipeline-Kette einen Fehler bei der Beendigung des Skripts auslöst, wird die Pipeline-Kette abgebrochen.

Zum Beispiel:

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

Auch wenn der Fehler abgefangen wird, wird die Pipeline-Kette abgebrochen:

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

Wenn ein Fehler nicht abschließend ist oder nur eine Pipeline abbricht, wird die Pipeline-Kette unter Berücksichtigung des Wertes 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

Verkettung von Pipelines anstelle von Befehlen

Pipeline-Kettenoperatoren können, wie der Name schon sagt, zur Verkettung von Pipelines und nicht nur von Befehlen verwendet werden. Dies entspricht dem Verhalten anderer Muscheln, macht es aber schwieriger, den Erfolg zu bestimmen:

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!' wird nicht ausgeführt, da Test-NotTwo gilt als gescheitert, nachdem der nicht abschließende Fehler erzeugt wurde.

Siehe auch