PowerShellのDelay-Bindingについて

以前投稿した[PowerShellにおけるパラメータバインディング](https://qiita.com/yasaram/items/5591e79e489904d025c3)の補足説明です。
これについてもあまり日本語では言及されていないようなので記事にしてみました。

##Delay-Binding概要
パイプラインオブジェクトは、あるパラメータにバインドされたら（パイプラインオブジェクトがパラメータの引数として結びつけられたら）他のパラメータにはバインドされないのが基本です。
しかし、これにはdelay-binding（ディレイ・バインディング）という例外があります。

delay-bindingでは、PowerShellの解析ルールのもとにすでにパイプラインオブジェクトがバインドされていたとしても（他のパラメータの引数になっていたとしても）そのオブジェクトを参照できます。
言い換えると、同一のパイプラインオブジェクトを同一のコマンド内で使用できるということになります。

##用例
デスクトップ上のファイルアイテムの拡張子をdelay-bindingを使って.txtから.ps1に変更する場合は次のようにします。

```powershell
>> Get-ChildItem $home\desktop -file|
    Rename-Item -NewName { $_ -replace "txt$", "ps1" }
```

Rename-Itemはアイテムの名前を変更するコマンドレットです。
-Pathパラメータには変更前のパス、-NewNameには変更後のアイテム名を指定します。

この例では、`-Path`にパイプラインオブジェクトが入力されていますが、
`-NewName`の引数となるスクリプトブロック内の同じパイプラインオブジェクトを`$_`で参照しています。

コード全体の処理の流れは次のようになります。

1. `Get-ChildItem`の実行
   - `Get-ChildItem`を実行、指定した場所からファイルアイテムのみを取得しパイプに流す
2. パラメータバインディング
   - パイプラインオブジェクトを`-Path`にバインドする
   - スクリプトブロックを処理（その際`$_`はパイプラインオブジェクトを参照）
   - スクリプトブロックを処理して得られた値を`-NewName`にバインドする
3. Rename-Itemの実行
   - 「2.」のパラーメータバインディングを結果をもとに処理

##Delay-Binding発動の条件
次の5つの条件が揃わないとdelay-bindingは発動しません。

1. delay-bindingが行われるパラメータが**「パイプライン入力を許可する」**であること（ByValue, ByPropertyNameいずれも可）
2. **パラメータ名を明記**すること（通常省略できるものでも）
3. パラメータの型がscriptblock型やobject型で**ない**こと
4. パラメータ引数はスクリプトブロックで、**パイプラインオブジェクトはスクリプトブロック内で使用されている**こと
5. パイプラインオブジェクトは**$_で参照**していること

##Trace-Commandによるバインディングプロセスの確認
Trace-Commandを使用すると、そのコマンドがどのタイミングでどのようにバインディングするかがわかります。

```powershell
Trace-Command -PSHost -Name ParameterBinding,cmdlet,metadata`
              -Expression {
                  Get-ChildItem　$home\Desktop -file |
                      Rename-Item -NewName { $_ -replace "txt$", "ps1" }
                }
```

`Rename-Item`のバインディングに関わるところを抜粋します。

```
デバッグ: ParameterBinding Information: 0 : BIND NAMED cmd line args [Rename-Item]
デバッグ: ParameterBinding Information: 0 :     Adding ScriptBlock to delay-bind list for parameter 'NewName'
```

`Rename-Item`における「パラメータ名がついたコマンドライン引数」のバインディングです。
`-NewName`がそれにあたりますが、PowerShellはスクリプトブロックを`-NewName`パラメータのdelay-bindリストに加えました。

```
デバッグ: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Rename-Item]
```

`Rename-Item`における「必須パラメータのチェック」を行っています。

```
デバッグ: ParameterBinding Information: 0 : BIND PIPELINE object to parameters: [Rename-Item]
デバッグ: ParameterBinding Information: 0 :     Invoking delay-bind ScriptBlock
デバッグ: ParameterBinding Information: 0 :     BIND arg [file_1.ps1] to parameter [NewName]
デバッグ: ParameterBinding Information: 0 :         COERCE arg to [System.String]
```
`Rename-Item`のパラメータにパイプラインオブジェクトをバインドします。
そこでdelay-bindリストにあったスクリプトブロックを発動し、`$_`が参照するfile_1.ps1を`-NewName`にバインドします。
引数は`System.String`型に変換します。

```
デバッグ: ParameterBinding Information: 0 :         BIND arg [file_1.ps1] to param [NewName] SUCCESSFUL
```

引数file_.ps1をNewNameパラメータにバインドできました。


##リファレンス
[about_Pipelines(PowerShell Documentation)](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_pipelines?view=powershell-6)
[about_Script_Block(PowerShell Documentation)](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_script_blocks?view=powershell-6)
[Rename-Item(PowerShell Documentation)](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/rename-item?view=powershell-6)
##おわりに
だらだらと長くなってしまいましたが、お役に立てると嬉しいです。
次はパラメータバインディングの優先順位について書こうかと思います。