about_Pipeline_Chain_Operators

Краткое описание

Описывает конвейеры цепочки с &&|| операторами и операторами в PowerShell.

Подробное описание

Начиная с PowerShell 7, PowerShell реализует && конвейеры условной цепочки и || операторы. Эти операторы известны в PowerShell как операторы цепочки конвейеров и похожи на списки AND-OR в оболочках POSIX, таких как bash, zsh и sh, а также символы условной обработки в командной оболочке Windows (cmd.exe).

Оператор && выполняет конвейер в правой части, если конвейер в левой части был выполнен успешно. И наоборот, оператор || выполняет конвейер в правой части, если конвейер в левой части не удалось выполнить.

Для определения того, был ли выполнен конвейер, эти операторы используют переменные $? и $LASTEXITCODE. Это позволяет использовать их с собственными командами, а не только с командлетами или функциями. Например:

# 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

Примеры

Две успешные команды

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

Первая команда завершается ошибкой, что приводит к неисполнимой секунде

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

Первая команда завершается успешно, поэтому вторая команда не выполняется

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

Первая команда завершается ошибкой, поэтому вторая команда выполняется

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

Успешность конвейера определяется значением переменной $? , которая PowerShell автоматически задает после выполнения конвейера на основе его состояния выполнения. Это означает, что операторы цепочки конвейеров имеют следующую эквивалентность:

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

работает так же, как и

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

и

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

работает так же, как и

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

Назначение из цепей конвейеров

Назначение переменной из цепочки конвейеров принимает объединение всех конвейеров в цепочке:

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

Если при назначении из цепочки конвейера возникает завершающаяся ошибка скрипта, назначение не выполняется:

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

"Result: $result"
Result:

Синтаксис оператора и приоритет

В отличие от других операторов, && а не на конвейерах, а не на выражениях, например-and+.||

&&и || имеют более низкий приоритет, чем piping (|) или перенаправление (>), но более высокий приоритет, чем операторы заданий (), назначение (&=) или точка с запятой (;). Это означает, что конвейеры в цепочке конвейеров могут быть перенаправлены по отдельности, а все цепочки конвейеров могут быть фоном, назначены переменным или разделены как инструкции.

Чтобы использовать более низкий синтаксис приоритета в цепочке конвейеров, рассмотрите возможность использования круглых скобок (...). Аналогичным образом для внедрения инструкции в цепочку конвейера можно использовать вложенные выражения $(...) . Это может быть полезно для объединения собственных команд с потоком управления:

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

По состоянию на PowerShell 7 поведение этих синтаксисов было изменено таким образом, чтобы $? он был задан, как ожидалось, когда команда завершается успешно или завершается сбоем в скобках или в подтекстах.

Как и большинство других операторов в PowerShell, && а || также являются левыми ассоциативными, то есть они группируются слева. Например:

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

будет группироваться как:

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

эквивалентен следующему:

Get-ChildItem -Path ./file.txt

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

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

Взаимодействие с ошибками

Операторы цепочки конвейеров не поглощают ошибки. Когда инструкция в цепочке конвейера вызывает ошибку, завершающая скрипт, цепочка конвейера завершается.

Например:

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

Даже при обнаружении ошибки цепочка конвейеров по-прежнему завершается:

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

Если ошибка не завершается или завершается только конвейер, цепочка конвейера продолжается, учитывая значение $?:

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

Цепочка конвейеров, а не команд

Операторы цепочки конвейеров, по их имени, можно использовать для цепочки конвейеров, а не только команд. Это соответствует поведению других оболочк, но может сделать успех более сложным для определения:

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

Обратите внимание, что не выполняется, так как Test-NotTwo считается, что Write-Output 'All done!' произошел сбой после создания неустранимой ошибки.

См. также