次の方法で共有


about_Pipeline_Chain_Operators

簡単な説明

PowerShell の && 演算子と || 演算子を使用してパイプラインを連結する方法について説明します。

長い説明

PowerShell 7 以降、PowerShell では、パイプラインを条件付きでチェーンするための && 演算子と || 演算子が実装されています。 これらの演算子は、パイプライン チェーン演算子として PowerShell で知られており、bash、zsh、sh などの POSIX シェルの AND-OR リストや、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

使用例

2 つの成功したコマンド

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

最初のコマンドが失敗し、2 番目のコマンドが実行されない

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

最初のコマンドは成功するため、2 番目のコマンドは実行されません

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

最初のコマンドが失敗するため、2 番目のコマンドが実行されます

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

パイプラインの成功は、$? 変数の値によって定義されます。この変数は、実行状態に基づいてパイプラインの実行後に PowerShell によって自動的に設定されます。 つまり、パイプライン チェーン演算子の等価性は次のとおりです。

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

と同じように動作します

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

and

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 は実行されないことに注意してください。

こちらも参照ください