about_Pipeline_Chain_Operators
Short description
Describes chaining pipelines with the &&
and ||
operators in PowerShell.
Long description
Beginning in PowerShell 7, PowerShell implements the &&
and ||
operators to
conditionally chain pipelines. 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.
These operators use the $?
and $LASTEXITCODE
variables to determine if a
pipeline failed. 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
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' }
and
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
Unlike 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 (;
). 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
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.
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 doesn't exist" &&
Get-Content -Raw ./file.txt
will group as:
(Get-ChildItem -Path ./file.txt || Write-Error "file.txt doesn't 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. 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
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
Feedback
Submit and view feedback for