Udostępnij za pośrednictwem


about_Pipeline_Chain_Operators

Krótki opis

Opisuje łączenie potoków za pomocą operatorów && i || w programie PowerShell.

Długi opis

Począwszy od programu PowerShell 7, program PowerShell implementuje operatory && i || do warunkowego łańcucha potoków. Te operatory są znane w programie PowerShell jako operatory łańcucha potoków i są podobne do list AND-OR list w powłokach POSIX, takich jak bash, zsh i sh, a także symbole przetwarzania warunkowego w powłoce poleceń systemu Windows (cmd.exe).

Operator && wykonuje potok po prawej stronie, jeśli potok po lewej stronie zakończył się pomyślnie. Z drugiej strony operator || wykonuje potok po prawej stronie, jeśli potok po lewej stronie zakończył się niepowodzeniem.

Te operatory używają zmiennych $? i $LASTEXITCODE, aby określić, czy potok zakończył się niepowodzeniem. Dzięki temu można używać ich z natywnymi poleceniami, a nie tylko z poleceniami cmdlet lub funkcjami. Na przykład:

# 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

Przykłady

Dwa pomyślne polecenia

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

Pierwsze polecenie kończy się niepowodzeniem, co powoduje, że drugie nie jest wykonywane

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

Pierwsze polecenie powiedzie się, więc drugie polecenie nie jest wykonywane

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

Pierwsze polecenie kończy się niepowodzeniem, więc drugie polecenie jest wykonywane

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

Powodzenie potoku jest definiowane przez wartość zmiennej $?, która program PowerShell automatycznie ustawia po wykonaniu potoku na podstawie stanu jego wykonania. Oznacza to, że operatorzy łańcucha potoków mają następującą równoważność:

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

działa tak samo jak

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

i

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

działa tak samo jak

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

Przypisywanie z łańcuchów potoków

Przypisanie zmiennej z łańcucha potoków obejmuje łączenie wszystkich potoków w łańcuchu:

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

Jeśli podczas przypisywania z łańcucha potoków wystąpi błąd zakończenia skryptu, przypisanie nie powiedzie się:

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

"Result: $result"
Result:

Składnia i pierwszeństwo operatora

W przeciwieństwie do innych operatorów && i || działają na potokach, a nie na wyrażeniach, takich jak + lub -and, na przykład.

&& i || mają niższy priorytet niż potok (|) lub przekierowanie (>), ale wyższy priorytet niż operatory zadań (&), przypisanie (=) lub średniki (;). Oznacza to, że potoki w łańcuchu potoków mogą być przekierowywane indywidualnie, a całe łańcuchy potoków mogą być w tle, przypisane do zmiennych lub oddzielone jako instrukcje.

Aby użyć składni niższego pierwszeństwa w łańcuchu potoków, rozważ użycie nawiasów (...). Podobnie, aby osadzić instrukcję w łańcuchu potoków, można użyć $(...) podrażenia. Może to być przydatne w przypadku łączenia natywnych poleceń z przepływem sterowania:

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

Od programu PowerShell 7 zachowanie tych składni zostało zmienione tak, aby $? było ustawione zgodnie z oczekiwaniami, gdy polecenie zakończy się powodzeniem lub niepowodzeniem w nawiasach lub podrażeniu.

Podobnie jak większość innych operatorów w programie PowerShell, && i || są również po lewej stronie, co oznacza, że grupują od lewej. Na przykład:

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

grupuje jako:

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

jest odpowiednikiem:

Get-ChildItem -Path ./file.txt

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

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

Interakcja z błędami

Operatory łańcucha potoków nie pochłaniają błędów. Gdy instrukcja w łańcuchu potoków zgłasza błąd zakończenia skryptu, łańcuch potoków zostanie zakończony.

Na przykład:

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

Nawet w przypadku przechwyconego błędu łańcuch potoków jest nadal przerywany:

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

Jeśli błąd nie kończy się lub kończy tylko potok, łańcuch potoków będzie kontynuowany, szanując wartość $?:

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

Łączenie potoków zamiast poleceń

Operatory łańcucha potoków, według ich nazwy, mogą służyć do tworzenia łańcuchów potoków, a nie tylko poleceń. Jest to zgodne z zachowaniem innych powłok, ale może utrudnić określenie:

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

Należy pamiętać, że Write-Output 'All done!' nie jest wykonywana, ponieważ Test-NotTwo jest uważana za nieudaną po wygenerowaniu błędu bez zakończenia.

Zobacz też