about_Pipelines

簡単な説明

PowerShell でコマンドをパイプラインに結合する

詳細な説明

パイプラインは、パイプライン演算子 () (|ASCII 124) によって接続される一連のコマンドです。 各パイプライン演算子は、前のコマンドの結果を次のコマンドに送信します。

最初のコマンドの出力は、2 番目のコマンドへの入力として処理するために送信できます。 その出力は、さらに別のコマンドに送信できます。 結果は、一連の単純なコマンドで構成される複雑なコマンド チェーンまたは パイプライン になります。

たとえば、 にします。

Command-1 | Command-2 | Command-3

この例では、出力されるオブジェクトは Command-1Command-2. Command-2 オブジェクトを処理し、オブジェクトを送信します Command-3Command-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出力をコマンドレットにStart-ServiceStop-Serviceパイプできます。

次のパイプラインの例では、コンピューター上で WMI サービスを開始します。

Get-Service wmi | Start-Service

別の例として、PowerShell レジストリ プロバイダーの出力または Get-ChildItem PowerShell レジストリ プロバイダー内のGet-Item出力をコマンドレットにNew-ItemPropertyパイプできます。 この例では、MyCompany レジストリ キーに、値 8124新しいレジストリ エントリ NoOfEmployees追加します。

Get-Item -Path HKLM:\Software\MyCompany |
  New-ItemProperty -Name NoOfEmployees -Value 8124

、などのGet-MemberGroup-ObjectWhere-ObjectSort-ObjectMeasure-Objectユーティリティ コマンドレットの多くは、パイプラインでほぼ排他的に使用されます。 任意の種類のオブジェクトをこれらのコマンドレットにパイプできます。 この例では、コンピューター上のすべてのプロセスを、各プロセスで開いているハンドルの数で並べ替える方法を示します。

Get-Process | Sort-Object -Property handles

オブジェクトをパイプ処理して、書式設定、エクスポート、出力のコマンドレット (例: Format-List, , Format-Table, Export-Clixml, Export-CSV) 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

重要

成功ストリームとエラー ストリームは、他のシェルの stdin ストリームと stderr ストリームに似ています。 ただし、stdin は入力用の PowerShell パイプラインに接続されていません。 詳細については、「about_Redirection」を参照してください

少し練習すれば、単純なコマンドをパイプラインに組み合わせると、時間と入力が節約され、スクリプト作成がより効率的になります。

パイプラインのしくみ

このセクションでは、入力オブジェクトをコマンドレット パラメーターにバインドし、パイプラインの実行中に処理する方法について説明します。

パイプライン入力を受け入れる

パイプライン処理をサポートするには、受信コマンドレットにパイプライン入力を受け入れるパラメーターが必要です。 コマンドを Get-Help Full オプションまたは Parameter オプションと共に使用して、パイプライン入力を受け入れるコマンドレットのパラメーターを決定します。

たとえば、パイプライン入力を受け入れるコマンドレットのパラメーターを Start-Service 決定するには、次のように入力します。

Get-Help Start-Service -Full

または

Get-Help Start-Service -Parameter *

コマンドレットのヘルプは、Start-ServiceInputObject パラメーターと 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 つずつ送信されます。 PowerShell では、プロパティを通じて Rows System.Data.DataTable 型も列挙されます。

自動列挙にはいくつかの例外があります。

  • ハッシュ テーブルの GetEnumerator() メソッド、インターフェイスまたはそのジェネリックに対応する IDictionary 型、および System.Xml.XmlNode 型を呼び出す必要があります。
  • System.String クラスは実装しますIEnumerableが、PowerShell では文字列オブジェクトは列挙されません。

次の例では、パイプラインから受信したオブジェクトの数をカウントするために 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-ProcessGet-Memberパイプする場合、PowerShell は各プロセス オブジェクトを一度に Get-Member1 つずつに送信します。 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
...

Note

Get-Member は重複を排除するため、オブジェクトがすべて同じ型の場合は、1 つのオブジェクト型のみが表示されます。

ただし、InputObject パラメーターGet-Member使用する場合は、Get-MemberSystem.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 文字列に変換されます。 この変換により、生データ出力が破損する可能性があります。

ただし、PowerShell 7.4 では、ネイティブ コマンドの stdout ストリームをファイルにリダイレクトするとき、またはバイト ストリーム データをネイティブ コマンドの stdin ストリームにパイプするときに、バイト ストリーム データを保持する試験的な機能が追加されましたPSNativeCommandPreserveBytePipe

たとえば、ネイティブ コマンド curl を使用すると、バイナリ ファイルをダウンロードし、それをディスクにリダイレクトを使用して保存することができます。

$uri = 'https://github.com/PowerShell/PowerShell/releases/download/v7.3.4/powershell-7.3.4-linux-arm64.tar.gz'

# native command redirected to a file
curl -s -L $uri > powershell.tar.gz

バイト ストリーム データは、別のネイティブ コマンドの stdin ストリームにパイプすることもできます。 次の例では、curl を使用して zip 形式の TAR ファイルをダウンロードしています。 ダウンロードしたファイル データは、tar コマンドにストリーミングされ、アーカイブの内容が抽出されます。

# native command output piped to a native command
curl -s -L $uri | tar -xzvf - -C .

PowerShell コマンドのバイト ストリーム出力をネイティブ コマンドの入力にパイプすることもできます。 次の例では、Invoke-WebRequest を使用して、前の例と同じ TAR ファイルをダウンロードしています。

# byte stream piped to a native command
(Invoke-WebRequest $uri).Content | tar -xzvf - -C .

# bytes piped to a native command (all at once as byte[])
,(Invoke-WebRequest $uri).Content | tar -xzvf - -C .

この機能では、stderr 出力を stdout にリダイレクトするとき、バイト ストリーム データをサポートしません。 stderr および stdout ストリームを結合すると、結合されたストリームは文字列データとして扱われます。

パイプライン エラーの調査

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-Item元のオブジェクトのプロパティを表示するために使用Get-Memberします。

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

重要

シェルで対話形式で作業する場合は、Ctrl V+使用して貼り付ける場合にのみ、行の先頭にパイプラインを含むコードを貼り付けます。 貼り付け操作を右クリックすると、一度に 1 行ずつ挿入されます。 行はパイプライン文字で終わらないため、PowerShell では入力が完了したと見なされ、入力された行が実行されます。

関連項目