about_Pipelines
簡単な説明
PowerShell でコマンドをパイプラインに結合する
長い説明
パイプラインは、パイプライン演算子 () によって接続される一連のコマンドです (|
ASCII 124)。 各パイプライン演算子は、前のコマンドの結果を次のコマンドに送信します。
最初のコマンドの出力は、2 番目のコマンドへの入力として処理するために送信できます。 その出力は、さらに別のコマンドに送信できます。 その結果、一連の単純なコマンドで構成される複雑なコマンド チェーンまたは パイプライン が生成されます。
たとえば、オブジェクトに適用された
Command-1 | Command-2 | Command-3
この例では、 を出力する Command-1
オブジェクトが に Command-2
送信されます。
Command-2
は オブジェクトを処理し、 に Command-3
送信します。 Command-3
はオブジェクトを処理し、パイプラインに送信します。 パイプラインにはこれ以上コマンドがないため、結果はコンソールに表示されます。
パイプラインでは、コマンドは左から右に順番に処理されます。 処理は 1 つの操作として処理され、出力は生成時に表示されます。
単純な例を次に示します。 次のコマンドは、メモ帳プロセスを取得し、停止します。
たとえば、オブジェクトに適用された
Get-Process notepad | Stop-Process
最初のコマンドでは、 コマンドレットを Get-Process
使用して、メモ帳プロセスを表す オブジェクトを取得します。 パイプライン演算子 (|
) を使用してプロセス オブジェクトを コマンドレットに Stop-Process
送信し、メモ帳プロセスを停止します。 指定したプロセスはパイプラインを Stop-Process
介して送信されるため、コマンドにプロセスを指定する Name パラメーターまたは ID パラメーターが含まれていないことに注意してください。
このパイプラインの例では、現在のディレクトリ内のテキスト ファイルを取得し、10,000 バイトを超えるファイルのみを選択し、長さで並べ替え、テーブル内の各ファイルの名前と長さを表示します。
Get-ChildItem -Path *.txt |
Where-Object {$_.length -gt 10000} |
Sort-Object -Property length |
Format-Table -Property name, length
このパイプラインは、指定した順序で 4 つのコマンドで構成されます。 次の図は、パイプライン内の次のコマンドに渡される各コマンドからの出力を示しています。
Get-ChildItem -Path *.txt
| (FileInfo objects for *.txt)
V
Where-Object {$_.length -gt 10000}
| (FileInfo objects for *.txt)
| ( Length > 10000 )
V
Sort-Object -Property Length
| (FileInfo objects for *.txt)
| ( Length > 10000 )
| ( Sorted by length )
V
Format-Table -Property name, length
| (FileInfo objects for *.txt)
| ( Length > 10000 )
| ( Sorted by length )
| ( Formatted in a table )
V
Name Length
---- ------
tmp1.txt 82920
tmp2.txt 114000
tmp3.txt 114000
パイプラインの使用
ほとんどの PowerShell コマンドレットは、パイプラインをサポートするように設計されています。 ほとんどの場合、Get コマンドレットの結果を同じ名詞の別のコマンドレットにパイプできます。
たとえば、コマンドレットの出力を Get-Service
または Stop-Service
コマンドレットにStart-Service
パイプできます。
次のパイプラインの例では、コンピューター上で WMI サービスを開始します。
Get-Service wmi | Start-Service
別の例として、 または Get-ChildItem
PowerShell レジストリ プロバイダー内のGet-Item
出力を コマンドレットにNew-ItemProperty
パイプできます。 次の使用例は、新しいレジストリ エントリ NoOfEmployees (値 8124) を MyCompany レジストリ キーに追加します。
Get-Item -Path HKLM:\Software\MyCompany |
New-ItemProperty -Name NoOfEmployees -Value 8124
、 などのGet-Member
Group-Object
Where-Object
Sort-Object
Measure-Object
ユーティリティ コマンドレットの多くは、パイプラインでのみ使用されます。 任意のオブジェクトの種類をこれらのコマンドレットにパイプできます。 この例では、コンピューター上のすべてのプロセスを、各プロセスで開いているハンドルの数で並べ替える方法を示します。
Get-Process | Sort-Object -Property handles
オブジェクトを、、、 などのFormat-List
Export-CSV
Format-Table
Export-Clixml
Out-File
書式設定、エクスポート、および出力コマンドレットにパイプできます。
この例では、 コマンドレットを Format-List
使用して、プロセス オブジェクトのプロパティの一覧を表示する方法を示します。
Get-Process winlogon | Format-List -Property *
ネイティブ コマンドの出力を PowerShell コマンドレットにパイプすることもできます。 例:
PS> ipconfig.exe | Select-String -Pattern 'IPv4'
IPv4 Address. . . . . . . . . . . : 172.24.80.1
IPv4 Address. . . . . . . . . . . : 192.168.1.45
IPv4 Address. . . . . . . . . . . : 100.64.108.37
重要
Success ストリームと Error ストリームは、他のシェルの stdin ストリームと stderr ストリームに似ています。 ただし、入力のために stdin は PowerShell パイプラインに接続されていません。 詳細については、「 about_Redirection」を参照してください。
少し練習すれば、単純なコマンドをパイプラインに組み合わせると、時間と入力が節約され、スクリプト作成がより効率的になることがわかります。
パイプラインのしくみ
このセクションでは、入力オブジェクトをコマンドレット パラメーターにバインドし、パイプラインの実行中に処理する方法について説明します。
パイプライン入力を受け入れる
パイプライン処理をサポートするには、受信コマンドレットにパイプライン入力を受け入れるパラメーターが必要です。 コマンドを Get-Help
Full オプションまたは Parameter オプションと共に使用して、パイプライン入力を受け入れるコマンドレットのパラメーターを決定します。
たとえば、パイプライン入力を受け入れるコマンドレットのパラメーターを Start-Service
決定するには、次のように入力します。
Get-Help Start-Service -Full
または
Get-Help Start-Service -Parameter *
コマンドレットのヘルプは、Start-Service
InputObject パラメーターと Name パラメーターのみがパイプライン入力を受け入れることを示しています。
-InputObject <ServiceController[]>
Specifies ServiceController objects representing the services to be started.
Enter a variable that contains the objects, or type a command or expression
that gets the objects.
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByValue)
Accept wildcard characters? false
-Name <String[]>
Specifies the service names for the service to be started.
The parameter name is optional. You can use Name or its alias, ServiceName,
or you can omit the parameter name.
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByPropertyName, ByValue)
Accept wildcard characters? false
パイプラインを介して オブジェクトを に Start-Service
送信すると、PowerShell はオブジェクトを InputObject パラメーターと Name パラメーターに関連付けようとします。
パイプライン入力を受け入れる方法
コマンドレット パラメーターは、次の 2 つの異なる方法のいずれかでパイプライン入力を受け入れます。
ByValue: パラメーターは、予想される .NET 型に一致する値、またはその型に変換できる値を受け取ります。
たとえば、 の Name パラメーター
Start-Service
は、値によるパイプライン入力を受け入れます。 文字列オブジェクトまたは文字列に変換できるオブジェクトを受け取ることができます。ByPropertyName: パラメーターは、入力オブジェクトに パラメーターと同じ名前のプロパティがある場合にのみ入力を受け入れます。
たとえば、 の Name パラメーター
Start-Service
は 、Name プロパティを持つオブジェクトを受け取ることができます。 オブジェクトのプロパティを一覧表示するには、 にGet-Member
パイプします。
一部のパラメーターでは、値またはプロパティ名の両方でオブジェクトを受け取ることができ、パイプラインからの入力を簡単に行うことができます。
パラメーターのバインド
あるコマンドから別のコマンドにオブジェクトをパイプすると、PowerShell はパイプされたオブジェクトを受信コマンドレットのパラメーターに関連付けようとします。
PowerShell のパラメーター バインド コンポーネントは、次の条件に従って、入力オブジェクトをコマンドレット パラメーターに関連付けます。
- パラメーターは、パイプラインからの入力を受け入れる必要があります。
- パラメーターは、送信されるオブジェクトの型、または予想される型に変換できる型を受け入れる必要があります。
- パラメーターは コマンドで使用されませんでした。
たとえば、 Start-Service
コマンドレットには多くのパラメーターがありますが、パイプライン入力を受け入れるのは Name と InputObject の 2 つだけです。 Name パラメーターは文字列を受け取り、InputObject パラメーターはサービス オブジェクトを受け取ります。 そのため、文字列、サービス オブジェクト、および文字列またはサービス オブジェクトに変換できるプロパティを持つオブジェクトをパイプ処理できます。
PowerShell は、パラメーター バインディングを可能な限り効率的に管理します。 PowerShell で特定のパラメーターにバインドするように提案したり強制したりすることはできません。 PowerShell でパイプされたオブジェクトをバインドできない場合、コマンドは失敗します。
バインド エラーのトラブルシューティングの詳細については、この記事で後述 する「パイプライン エラーの調査 」を参照してください。
一度に 1 回の処理
コマンドへのオブジェクトのパイプ処理は、 コマンドの パラメーターを使用してオブジェクトを送信するのとよく似ています。 パイプラインの例を見てみましょう。 この例では、パイプラインを使用してサービス オブジェクトのテーブルを表示します。
Get-Service | Format-Table -Property Name, DependentServices
機能的には、 の InputObject パラメーター Format-Table
を使用してオブジェクト コレクションを送信するようなものです。
たとえば、 InputObject パラメーターを使用して渡される変数にサービスのコレクションを保存できます。
$services = Get-Service
Format-Table -InputObject $services -Property Name, DependentServices
または、 InputObject パラメーターに コマンドを埋め込むことができます。
Format-Table -InputObject (Get-Service) -Property Name, DependentServices
ただし、重要な違いがあります。 複数のオブジェクトを 1 つのコマンドにパイプすると、PowerShell はオブジェクトをコマンドに一度に 1 つずつ送信します。 コマンド パラメーターを使用すると、オブジェクトは 1 つの配列オブジェクトとして送信されます。 この小さな違いは大きな影響を与える。
パイプラインを実行すると、PowerShell はインターフェイスを実装する任意の型を IEnumerable
自動的に列挙し、一度に 1 つずつパイプラインを介してメンバーを送信します。 例外は であり、 [hashtable]
メソッドの呼び出しが GetEnumerator()
必要です。
次の例では、配列とハッシュテーブルをコマンドレットにパイプ処理して Measure-Object
、パイプラインから受信したオブジェクトの数をカウントします。 配列には複数のメンバーがあり、ハッシュテーブルには複数のキーと値のペアがあります。 配列のみが一度に 1 つずつ列挙されます。
@(1,2,3) | Measure-Object
Count : 3
Average :
Sum :
Maximum :
Minimum :
Property :
@{"One"=1;"Two"=2} | Measure-Object
Count : 1
Average :
Sum :
Maximum :
Minimum :
Property :
同様に、コマンドレットからコマンドレットに複数のプロセス オブジェクトをGet-Process
Get-Member
パイプ処理する場合、PowerShell は各プロセス オブジェクトを一度に 1 つずつ にGet-Member
送信します。 Get-Member
には、プロセス オブジェクトの .NET クラス (型) とそのプロパティとメソッドが表示されます。
Get-Process | Get-Member
TypeName: System.Diagnostics.Process
Name MemberType Definition
---- ---------- ----------
Handles AliasProperty Handles = Handlecount
Name AliasProperty Name = ProcessName
NPM AliasProperty NPM = NonpagedSystemMemorySize
...
注意
Get-Member
は重複を排除するため、オブジェクトがすべて同じ型の場合は、1 つのオブジェクト型のみが表示されます。
ただし、 の InputObject パラメーターGet-Member
を使用すると、Get-Member
System.Diagnostics.Process オブジェクトの配列が 1 つの単位として受け取られます。 オブジェクトの配列のプロパティが表示されます。 (System.Object 型名の後の配列記号 ([]
) に注意してください。
たとえば、オブジェクトに適用された
Get-Member -InputObject (Get-Process)
TypeName: System.Object[]
Name MemberType Definition
---- ---------- ----------
Count AliasProperty Count = Length
Address Method System.Object& Address(Int32 )
Clone Method System.Object Clone()
...
この結果は、意図した内容ではない可能性があります。 しかし、それを理解したら、それを使用することができます。 たとえば、すべての配列オブジェクトに Count プロパティがあります。 これを使用して、コンピューターで実行されているプロセスの数をカウントできます。
たとえば、オブジェクトに適用された
(Get-Process).count
パイプラインに送信されたオブジェクトは一度に 1 つずつ配信されることを忘れないでください。
パイプラインでのネイティブ コマンドの使用
PowerShell を使用すると、パイプラインにネイティブ外部コマンドを含めることができます。 ただし、PowerShell のパイプラインはオブジェクト指向であり、生バイト データはサポートされていないことに注意してください。
生バイト データを出力するネイティブ プログラムからの出力をパイプ処理またはリダイレクトすると、出力が .NET 文字列に変換されます。 この変換により、生データ出力が破損する可能性があります。
回避策として、 または sh -c
を使用してネイティブ コマンドをcmd.exe /c
呼び出し、ネイティブ シェルによって提供される 演算子と >
演算子を使用|
します。
パイプライン エラーの調査
PowerShell でパイプされたオブジェクトを受信コマンドレットのパラメーターに関連付けることができない場合、コマンドは失敗します。
次の例では、あるレジストリ キーから別のレジストリ キーにレジストリ エントリを移動しようとします。 コマンドレットは Get-Item
宛先パスを取得し、コマンドレットに Move-ItemProperty
パイプ処理します。 コマンドは Move-ItemProperty
、移動するレジストリ エントリの現在のパスと名前を指定します。
Get-Item -Path HKLM:\Software\MyCompany\sales |
Move-ItemProperty -Path HKLM:\Software\MyCompany\design -Name product
コマンドが失敗し、PowerShell に次のエラー メッセージが表示されます。
Move-ItemProperty : The input object can't be bound to any parameters for
the command either because the command doesn't take pipeline input or the
input and its properties do not match any of the parameters that take
pipeline input.
At line:1 char:23
+ $a | Move-ItemProperty <<<< -Path HKLM:\Software\MyCompany\design -Name p
調査するには、 コマンドレットを Trace-Command
使用して PowerShell のパラメーター バインド コンポーネントをトレースします。 次の例では、パイプラインの実行中にパラメーター バインドをトレースします。 PSHost パラメーターは、トレース結果をコンソールに表示し、FilePath パラメーターは後で参照できるようにトレース結果をファイルにdebug.txt
送信します。
Trace-Command -Name ParameterBinding -PSHost -FilePath debug.txt -Expression {
Get-Item -Path HKLM:\Software\MyCompany\sales |
Move-ItemProperty -Path HKLM:\Software\MyCompany\design -Name product
}
トレースの結果は長くなりますが、コマンドレットにバインドされている値と、コマンドレットに Get-Item
バインドされている名前付き値が Move-ItemProperty
表示されます。
...
BIND NAMED cmd line args [`Move-ItemProperty`]
BIND arg [HKLM:\Software\MyCompany\design] to parameter [Path]
...
BIND arg [product] to parameter [Name]
...
BIND POSITIONAL cmd line args [`Move-ItemProperty`]
...
最後に、 の Destination パラメーター Move-ItemProperty
へのパスのバインドが失敗したことを示します。
...
BIND PIPELINE object to parameters: [`Move-ItemProperty`]
PIPELINE object TYPE = [Microsoft.Win32.RegistryKey]
RESTORING pipeline parameter's original values
Parameter [Destination] PIPELINE INPUT ValueFromPipelineByPropertyName NO
COERCION
Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName NO
COERCION
...
コマンドレットを Get-Help
使用して、 Destination パラメーターの属性を表示します。
Get-Help Move-ItemProperty -Parameter Destination
-Destination <String>
Specifies the path to the destination location.
Required? true
Position? 1
Default value None
Accept pipeline input? True (ByPropertyName)
Accept wildcard characters? false
結果は、 Destination が "プロパティ名で" パイプライン入力のみを受け取るという結果を示しています。 したがって、パイプされたオブジェクトには Destination という名前のプロパティが必要です。
を使用して Get-Member
、 から Get-Item
取得されるオブジェクトのプロパティを確認します。
Get-Item -Path HKLM:\Software\MyCompany\sales | Get-Member
出力は、アイテムが Destination プロパティを持たない Microsoft.Win32.RegistryKey オブジェクトであることを示しています。 これは、コマンドが失敗した理由を説明します。
Path パラメーターは、名前または値によるパイプライン入力を受け入れます。
Get-Help Move-ItemProperty -Parameter Path
-Path <String[]>
Specifies the path to the current location of the property. Wildcard
characters are permitted.
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByPropertyName, ByValue)
Accept wildcard characters? true
コマンドを修正するには、 コマンドレットで宛先を Move-ItemProperty
指定し、 を使用 Get-Item
して移動する項目の パス を取得する必要があります。
たとえば、オブジェクトに適用された
Get-Item -Path HKLM:\Software\MyCompany\design |
Move-ItemProperty -Destination HKLM:\Software\MyCompany\sales -Name product
組み込み行の継続
既に説明したように、パイプラインはパイプライン演算子 (|
) によって接続される一連のコマンドであり、通常は 1 行で記述されます。 ただし、読みやすくするために、PowerShell ではパイプラインを複数行に分割できます。
パイプ演算子が行の最後のトークンである場合、PowerShell パーサーは次の行を現在のコマンドに結合してパイプラインの構築を続行します。
たとえば、次の単一行パイプラインです。
Command-1 | Command-2 | Command-3
は、次のように記述できます。
Command-1 |
Command-2 |
Command-3
後続の行の先頭のスペースは重要ではありません。 インデントによって読みやすさが向上します。
PowerShell 7 では、行の先頭にパイプライン文字を含むパイプラインの継続のサポートが追加されています。 次の例では、この新しい機能を使用する方法を示します。
# Wrapping with a pipe at the beginning of a line (no backtick required)
Get-Process | Where-Object CPU | Where-Object Path
| Get-Item | Where-Object FullName -match "AppData"
| Sort-Object FullName -Unique
# Wrapping with a pipe on a line by itself
Get-Process | Where-Object CPU | Where-Object Path
|
Get-Item | Where-Object FullName -match "AppData"
|
Sort-Object FullName -Unique
重要
シェルで対話形式で作業する場合は、CtrlVキー+を使用して貼り付ける場合にのみ、行の先頭にパイプラインを含むコードを貼り付けます。 貼り付け操作を右クリックすると、一度に 1 行ずつ行が挿入されます。 行はパイプライン文字で終わらないため、PowerShell では入力が完了したと見なされ、入力されたとおりにその行が実行されます。