共用方式為


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 產生非終止錯誤之後,被視為失敗。

另請參閱