about_Pipeline_Chain_Operators
簡短描述
描述在 PowerShell 中使用 &&
和 ||
運算子鏈結管線。
完整描述
從 PowerShell 7 開始,PowerShell 會 &&
實作 和 ||
運算符,以有條件地鏈結管線。 這些運算符在 PowerShell 中稱為 管線鏈結運算符,類似於 POSIX 殼層中的 AND-OR 清單 ,例如 bash、zsh 和 sh,以及 Windows 命令殼層中的 條件式處理符號 (cmd.exe) 。
如果左側管線成功,&&
運算子就會執行右側管線。 反過來說,如果左側管線失敗,||
運算子就會執行右側管線。
這些運算子會使用 $?
和 $LASTEXITCODE
變數來判斷管線是否失敗。 這可讓您將其與原生命令搭配使用,而不只是與 Cmdlet 或函式搭配使用。 例如:
# 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
等+
表達式上運作。
&&
和 ||
的優先順序低於管線 () |
或重新 >
導向 () ,但優先順序高於作業運算符 &
() 、指派 (=
) 或分號 (;
) 。 這表示管線鏈結內的管線可以個別重新導向,而且整個管線鏈結可以背景處理、指派給變數或分隔為語句。
若要在管線鏈結中使用優先順序較低的語法,請考慮使用括弧 (...)
。 同樣地,若要在管線鏈結內嵌 語句,可以使用子表達式 $(...)
。 這在結合原生命令與控制流程時很有用:
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
請注意, Write-Output 'All done!'
不會執行,因為 Test-NotTwo
產生非終止錯誤之後,被視為失敗。