关于管道链运算符About Pipeline Chain Operators

简短说明Short description

介绍如何 && 在 PowerShell 中将管道与 and || 运算符链接。Describes chaining pipelines with the && and || operators in PowerShell.

长说明Long description

从 PowerShell 7 开始,PowerShell 实现 &&|| 运算符以有条件地链接管道。Beginning in PowerShell 7, PowerShell implements the && and || operators to conditionally chain pipelines. 这些运算符在 PowerShell 中已知为 管道链运算符 ,并且类似于 POSIX shell 中的 和-或列表 (如 bash、zsh 和 Sh)以及 Windows 命令行界面中的 条件处理符号 ( # A0) 。These operators are known in PowerShell as pipeline chain operators , and are similar to AND-OR lists in POSIX shells like bash, zsh and sh, as well as conditional processing symbols in the Windows Command Shell (cmd.exe).

如果左侧管道成功,则 && 运算符将执行右侧管道。The && operator executes the right-hand pipeline, if the left-hand pipeline succeeded. 相反,如果左侧管道失败,则 || 运算符将执行右侧管道。Conversely, the || operator executes the right-hand pipeline if the left-hand pipeline failed.

这些运算符使用 $?$LASTEXITCODE 变量来确定管道是否失败。These operators use the $? and $LASTEXITCODE variables to determine if a pipeline failed. 这允许你将其与本机命令一起使用,而不只是与 cmdlet 或函数一起使用。This allows you to use them with native commands and not just with cmdlets or functions. 例如:For example:

# 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

示例Examples

两个成功的命令Two successful commands

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

第一条命令失败,导致第二个命令失败First command fails, causing second not to be executed

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

第一个命令成功,因此不执行第二个命令First command succeeds, so the second command is not executed

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

第一条命令失败,因此将执行第二个命令First command fails, so the second command is executed

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

管道成功由变量的值定义 $? ,在基于其执行状态执行管道之后,PowerShell 会自动设置该变量的值。Pipeline success is defined by the value of the $? variable, which PowerShell automatically sets after executing a pipeline based on its execution status. 这意味着管道链运算符具有以下等效性:This means that pipeline chain operators have the following equivalence:

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

的工作方式与works the same as

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

andand

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

的工作方式与works the same as

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

从管道链分配Assignment from pipeline chains

从管道链分配变量会使该链中的所有管道串联:Assigning a variable from a pipeline chain takes the concatenation of all the pipelines in the chain:

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

如果在分配管道链期间出现脚本终止错误,则分配不会成功:If a script-terminating error occurs during assignment from a pipeline chain, the assignment does not succeed:

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

"Result: $result"
Result:

运算符语法和优先级Operator syntax and precedence

与其他运算符不同, && 并对 || 管道进行操作,而不是在或等表达式上操作 + -andUnlike other operators, && and || operate on pipelines, rather than on expressions like + or -and, for example.

&& 和的 || 优先级低于管道 (|) 或重定向 (>) ,但优先级高于作业运算符 () & 、赋值 (=) 或分号 (;) 。&& and || have a lower precedence than piping (|) or redirection (>), but a higher precedence than job operators (&), assignment (=) or semicolons (;). 这意味着,可以单独重定向管道链内的管道,并且可以将整个管道链 backgrounded、赋值给变量或分隔为语句。This means that pipelines within a pipeline chain can be individually redirected, and that entire pipeline chains can be backgrounded, assigned to variables, or separated as statements.

若要在管道链中使用低优先级语法,请考虑使用括号 (...)To use lower precedence syntax within a pipeline chain, consider the use of parentheses (...). 同样,若要在管道链中嵌入语句, $(...) 可以使用子表达式。Similarly, to embed a statement within a pipeline chain, a subexpression $(...) can be used. 这可用于结合控制流的本机命令:This can be useful for combining native commands with control flow:

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 起,这些语法的行为已更改,以便 $? 在命令在括号或子表达式内成功或失败时设置为预期。As of PowerShell 7, the behaviour of these syntaxes has been changed so that $? is set as expected when a command succeeds or fails within parentheses or a subexpression.

与 PowerShell 中的大多数其他运算符一样, &&|| 都是 左结合 运算符,这意味着它们从左侧进行分组。Like most other operators in PowerShell, && and || are also left-associative , meaning they group from the left. 例如:For example:

Get-ChildItem -Path ./file.txt || Write-Error "file.txt does not exist" && Get-Content -Raw ./file.txt

将按以下方式分组:will group as:

[Get-ChildItem -Path ./file.txt || Write-Error "file.txt does not exist"] && Get-Content -Raw ./file.txt

等效于:being equivalent to:

Get-ChildItem -Path ./file.txt

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

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

错误交互Error interaction

管道链运算符不会吸收错误。Pipeline chain operators do not absorb errors. 当管道链中的语句引发脚本终止错误时,管道链将终止。When a statement in a pipeline chain throws a script-terminating error, the pipeline chain is terminated.

例如:For example:

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

即使捕获到错误,管道链仍会终止:Even when the error is caught, the pipeline chain is still terminated:

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

如果错误是非终止的,或者只是终止了管道,则管道链将继续,并遵从的值 $?If an error is non-terminating, or only terminates a pipeline, the pipeline chain continues, respecting the value of $?:

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

链接管道,而不是命令Chaining pipelines rather than commands

管道链运算符(按其名称)可用于链接管道,而不只是命令。Pipeline chain operators, by their name, can be used to chain pipelines, rather than just commands. 这与其他 shell 的行为匹配,但可能会导致难以 成功 确定: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

请注意, Write-Output 'All done!' 不会执行,因为在 Test-NotTwo 生成非终止错误后被视为已失败。Note that Write-Output 'All done!' is not executed, since Test-NotTwo is deemed to have failed after generating the non-terminating error.

另请参阅See also