2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PowerShellにおけるパラメータバインディング

Last updated at Posted at 2019-07-18

以前書いた記事Powershellにおけるバインディングについてのまとめを改めて整理した記事です。
パラメータバインディングについてよくわからなくなったけど調べても日本語で書かれた記事は少ないように感じたので書きました。

##パラメータバインディング
コマンドにより生成されたオブジェクトは、パイプの先にある次のコマンドの引数になろうとします。
PowerShellは次のコマンドのパラメータとパイプラインオブジェクトの型にもとづいて解析を行い、それに応じた挙動をします。
そうしたパラメータバインディング(パラメータとパイプラインオブジェクトの関連づけ)は次の3つの条件を満たすパラメータを探します。

  1. パラメータがパイプライン入力(パイプラインオブジェクトがそのパラメータの引数になること)を許可する(ヘルプで確認可能)
  2. パラメータの型(パラメータが要求する型)とパイプラインオブジェクトの型が一致している、あるいは型変換が可能である
  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.ValueTypeSystem.Objectから継承されたものなので、型変換が可能となり条件を満たします。

条件3. パラメータがすでにそのコマンド内で使用されていない
上記の例ではWrite-Hostの-Objectパラメータが使われていない(他に引数がない)ため、条件を満たしています。

##パイプラインオブジェクトの入力パターン
###2種類のパイプライン入力
パイプライン入力を許可するパラメータはByValueByPropertyNameの2種類に分けられます。「パイプライン入力を許可する」がTrueであっても、このByValueByPropertyNameの入力条件を満たしていないとパイプライン入力は行われません。
そのパラメータがどちらに属するかはヘルプのパラメータ欄「パイプライン入力を許可する」の右に記載されています。

###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が解析する順番が決まっているためです。これについては別記事にする予定です)。

-NameByPropertyNameとなっています。Get-ChildItemで得られる値のプロパティにはNameがあるため、パラメータ名と完全に一致します。
他のByPropertyNameとされているパラメータ(-Credential, -Path)はオブジェクトのプロパティ名と一致しないためバインドされません。

そのため、プロパティ値が-Nameにバインドされることとなります。

##リファレンス
About_Pipelines(PowerShell Docs)

##おわりに
わかりづらいかもしれません。
次はPowerShellのdelay-bindingについてまとめようかと考えています。

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?