以前書いた記事Powershellにおけるバインディングについてのまとめを改めて整理した記事です。
パラメータバインディングについてよくわからなくなったけど調べても日本語で書かれた記事は少ないように感じたので書きました。
##パラメータバインディング
コマンドにより生成されたオブジェクトは、パイプの先にある次のコマンドの引数になろうとします。
PowerShellは次のコマンドのパラメータとパイプラインオブジェクトの型にもとづいて解析を行い、それに応じた挙動をします。
そうしたパラメータバインディング(パラメータとパイプラインオブジェクトの関連づけ)は次の3つの条件を満たすパラメータを探します。
- パラメータがパイプライン入力(パイプラインオブジェクトがそのパラメータの引数になること)を許可する(ヘルプで確認可能)
- パラメータの型(パラメータが要求する型)とパイプラインオブジェクトの型が一致している、あるいは型変換が可能である
- パラメータがすでにそのコマンド内で使用されていない(パラメータが他の引数とバインドされていない)
パラメータバインディングの対象になるパイプラインオブジェクトは自動的に入力されるため実際に入力する必要はありません。
##パラメータバインディングの例と検証
###ベースとなるコード
次の例を使ってパラメータバインディングがどのように行われるか見ていきます。
これは、配列の要素ひとつひとつがパイプラインオブジェクトとなり次のコマンドに渡しています。
>> 1..3 | Write-Host
1
2
3
###成立条件を満たしているかを検証
条件1. パラメータがパイプライン入力を許可
Write-Host
のヘルプのパラメータ欄の「パイプラインの入力を許可する」がTrue
になっているものを探します。
>> Get-Help Write-Host -Parameter * |
Where-Object {$_.PipelineInput -match "true" }
# $_: 現在のパイプラインオブジェクト
-Object <Object>
Specifies objects to display in the console.
必須 false
位置 0
既定値 None
パイプライン入力を許可する True (ByValue)
ワイルドカード文字を許可する false
以上から、-Objectは条件を満たしています。
条件2. パラメータの型とパイプラインオブジェクトの型が一致している、あるいは型変換が可能
【パイプラインオブジェクトの型】
GetType()
プロパティで調べます。
1..3 | ForEach-Object{$_.GetType()}
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
【パラメータが要求する型】
先ほど開いたヘルプから、System.Object
であることがわかります。
System.ValueType
はSystem.Object
から継承されたものなので、型変換が可能となり条件を満たします。
条件3. パラメータがすでにそのコマンド内で使用されていない
上記の例ではWrite-Hostの-Objectパラメータが使われていない(他に引数がない)ため、条件を満たしています。
##パイプラインオブジェクトの入力パターン
###2種類のパイプライン入力
パイプライン入力を許可するパラメータはByValue
とByPropertyName
の2種類に分けられます。「パイプライン入力を許可する」がTrueであっても、このByValue
とByPropertyName
の入力条件を満たしていないとパイプライン入力は行われません。
そのパラメータがどちらに属するかはヘルプのパラメータ欄「パイプライン入力を許可する」の右に記載されています。
###ByValueとByPropertyValueが入力オブジェクトに求める条件・パラメータへの入力内容・例
ByValue
【条件】パイプラインオブジェクトの型とパラメータで指定された型が一致
【入力内容】パイプラインオブジェクトの値
【例】前項を参照
ByPropertyName
【条件】パイプラインオブジェクトのプロパティ名とパラメータ名が一致
【入力内容】パイプラインオブジェクトのプロパティ値
【例】次項を参照
##ByPropertyNameによるバインディング
オブジェクトがパイプラインを通じてNew-Item
に渡される例を以下に示します。
併せて、パラメータバインディングの条件を満たさない場合はパイプラインオブジェクトをどのように使うかを示します。
###新規ファイルの作成(パイプライン入力を許可しない場合のパイプラインオブジェクトの受け渡し)
パイプラインオブジェクトの受け渡し方法を確認するためのファイルを作成します。
デスクトップ上に2つのフォルダ(PipelineInput1とPipelineInput2とする)を作成し、PipelineInput1をカレントディレクトリにします。
>> cd $home\desktop
>> 1, 2 | ForEach-Object{ md PipelineInput$_ }
>> cd PipelineInput1
次にNew-Item
を使い、PipelineInput1内にテキストファイルを3ファイル作成します。
>> 1..3|
ForEach-Object {
New-Item -Name "file_$_.txt" -ItemType File -Value "$_`と書き込みました"
}
実行するとファイル名が確認できます。
ディレクトリ: C:\Users\xxxxx\Desktop\PipelineInput1
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 7/16/2019 1:01 PM 25 file_1.txt
-a---- 7/16/2019 1:01 PM 25 file_2.txt
-a---- 7/16/2019 1:01 PM 25 file_3.txt
ファイルに書き込まれた内容も確認します。
>> Get-Content file_*.txt -encoding UTF8
1と書き込みました
2と書き込みました
3と書き込みました
###既存の情報を使った新規ファイルの作成(パイプライン入力を利用したオブジェクトの受け渡し)
PipelineInput1に作成したファイルの情報を取得し、そこからPipepineInput2に新規のファイルを作成します。
カレントディレクトリをPipelineInput2に変更した後、Get-ChildItem
で取得したPipelineInput1の情報をNew-Item
に渡します。
作成するものはファイルとします。
>> cd $home\desktop\PipelineInput2
>> Get-ChildItem $home\desktop\PipelineInput1 | New-Item -ItemType File
ディレクトリ: C:\Users\xxxxx\Desktop\PipelineInput2
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 7/16/2019 1:10 PM 48 file_1.txt
-a---- 7/16/2019 1:10 PM 48 file_2.txt
-a---- 7/16/2019 1:10 PM 48 file_3.txt
先ほどと同様、その内容も確認します。
>> Get-Content file_*.txt -encoding UTF8
C:\Users\xxxxx\desktop\PipelineInput1\file_1.txt
C:\Users\xxxxx\desktop\PipelineInput1\file_2.txt
C:\Users\xxxxx\desktop\PipelineInput1\file_3.txt
New-Item
で作成するファイルの種類しか指定していないにもかかわらず、ファイル名を指定し書き込みもできました。
それでは、どのようにパラメータバインディングが行われたのでしょうか。
そこで、New-Item
でパイプラインオブジェクトを受け入れるパラメータを確認します。
>> Get-Help New-Item -Parameter * | Where-Object {$_.PipelineInput -match "true" }
#抜粋
-ItemType <String>
パイプライン入力を許可する True (ByPropertyName)
-Name <String>
パイプライン入力を許可する True (ByPropertyName)
-Value <Object>
パイプライン入力を許可する True (ByPropertyName, ByValue)
このうち、-ItemType
はすでに対応する引数を指定しているためパイプライン入力の対象になりません。
次に、-Value
を見てみるとByPropertyName, ByValue
となっています。
ByValue
から確認すると、Get-ChildItem
で得られるオブジェクトの型はSystem.IO.FileSystemInfo
、パラメータの型はSystem.Object
なので型の変換が可能となり、-Value
にはオブジェクトの値がバインドされます。
この時点でByPropertyName
にはもうバインドされません(これはPowerShellが解析する順番が決まっているためです。これについては別記事にする予定です)。
-Name
はByPropertyName
となっています。Get-ChildItem
で得られる値のプロパティにはName
があるため、パラメータ名と完全に一致します。
他のByPropertyName
とされているパラメータ(-Credential
, -Path
)はオブジェクトのプロパティ名と一致しないためバインドされません。
そのため、プロパティ値が-Name
にバインドされることとなります。
##リファレンス
About_Pipelines(PowerShell Docs)
##おわりに
わかりづらいかもしれません。
次はPowerShellのdelay-bindingについてまとめようかと考えています。