about_Pipeline_Chain_Operators

簡単な説明

PowerShell の and || 演算子を使用した && パイプラインのチェーンについて説明します。

長い説明

Powershell 7 以降では、PowerShell は and 演算子を実装 && して、条件に応じて || パイプラインをチェーンします。 これらの演算子は、PowerShell で パイプラインチェーン演算子 として知られています。また、bash、zsh および sh などの POSIX シェルの and または listと同様に、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' }

および

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 などの式 + ではなく、パイプラインを操作し || ます。

&&|| は、パイプ ( | ) またはリダイレクト ( > ) よりも優先順位が低くなりますが、ジョブ演算子 ( & )、割り当て ( = )、またはセミコロン ( ; ) より優先順位が高くなります。 つまり、パイプラインチェーン内のパイプラインは個別にリダイレクトできます。また、パイプラインチェーン全体を backgrounded、変数に代入、またはステートメントとして分離することができます。

パイプラインチェーン内で優先順位の低い構文を使用するには、かっこ (...) を使用することを検討してください。 同様に、パイプラインチェーン内にステートメントを埋め込むには、部分式 $(...) を使用できます。 これは、ネイティブコマンドと制御フローを組み合わせる場合に便利です。

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 does not exist" && Get-Content -Raw ./file.txt

次のようにグループ化:

[Get-ChildItem -Path ./file.txt || Write-Error "file.txt does not 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 です。

関連項目