about_Pipelines

適用対象: Windows PowerShell 2.0, Windows PowerShell 3.0, Windows PowerShell 4.0, Windows PowerShell 5.0

トピック

about_pipelines

概要

Windows PowerShell でのパイプラインへのコマンドの組み合わせ

詳細説明

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

パイプラインを使用して、1 つのコマンドからの出力であるオブジェクトを、別のコマンドの入力として送信し、処理することができます。そのコマンドの出力をさらに別のコマンドに送信できます。結果は非常に強力なコマンドのチェーン、つまり一連の単純なコマンドで構成される「パイプライン」になります。

例:

Command-1 | Command-2 | Command-3  

この例では、Command-1 で生成されるオブジェクトは Command-2 に送信されます。Command-2 はオブジェクトを処理し、Command-3 に送信します。Command-3 はオブジェクトを処理し、それらをパイプラインに送信します。パイプラインにはそれ以上コマンドがないので、結果がコンソールに表示されます。

パイプラインでは、コマンドが左から右に、出現する順序で処理されます。処理は単一の操作として扱われ、出力は生成されるとおりに表示されます。

簡単な例を次に示します。次のコマンドは、メモ帳のプロセスを取得し、それを停止します。

         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 )
                  |   (    .txt         )
                  |
                  V                   

       Where-Object {$_.length -gt 10000}

                  |
                  |   (FileInfo objects )
                  |   (    .txt         )
                  |   ( Length > 10000  )
                  |
                  V

       Sort-Object -property Length

                  |
                  |   (FileInfo objects  )
                  |   (    .txt          )
                  |   ( Length > 10000   )
                  |   ( Sorted by length )
                  |
                  V

       Format-Table -property name, length

                  |   
                  |   (FileInfo objects     )
                  |   (    .txt             )
                  |   ( Length > 10000      )
                  |   ( Sorted by length    )
                  |   (Formatted in a table )
                  |
                  V
        Name                       Length
        ----                       ------
        tmp1.txt                    82920
        tmp2.txt                   114000
        tmp3.txt                   114000

パイプラインの使用方法

Windows PowerShell コマンドレットは、パイプラインで使用されるように設計されました。たとえば、通常、Get コマンドレットの結果を、同じ名詞のアクション コマンドレット (Set、Start、Stop、または Rename コマンドレットなど) にパイプ処理することができます。

たとえば、Get-Service コマンドレットからの任意のサービスを Start-Service または Stop-Service コマンドレットにパイプ処理できます (ただし、この方法では、無効になっているサービスを再起動できません)。

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

get-service wmi | start-service

Windows PowerShell プロバイダーのオブジェクトを取得および設定するコマンドレット (Item および ItemProperty コマンドレットなど) も、パイプラインで処理するように設計されています。

たとえば、Windows PowerShell レジストリ プロバイダー内の Get-Item または Get-ChildItem コマンドの結果を New-ItemProperty コマンドレットにパイプ処理できます。このコマンドは、新しいレジストリ エントリである NoOfEmployees を 8124 という値で、MyCompany レジストリ キーに追加します。

       get-item -path HKLM:\Software\MyCompany | new-Itemproperty -name NoOfEmployees -value 8124

Get-Member、Where-Object、Sort-Object、Group-Object、および Measure-Object といったユーティリティ コマンドレットの多くは、ほぼ例外なくパイプラインで使用されます。これらのコマンドレットに任意のオブジェクトをパイプ処理できます。

たとえば、コンピューター上のすべてのプロセスを Sort-Object コマンドにパイプ処理し、プロセス内のハンドルの数で並べ替えることができます。

get-process | sort-object -property handles

また、任意のオブジェクトを Format-List および Format-Table などの書式設定コマンドレット、Export-Clixml および Export-CSV などの Export コマンドレット、Out-Printer などの Out コマンドレットにパイプできます。

たとえば、Winlogon プロセスを Format-list コマンドレットにパイプして、プロセスのすべてのプロパティをリストに表示できます。

get-process winlogon | format-list -property *

少し実務を経験すると、簡単なコマンドをパイプラインに組み合わせることによって、時間と入力が節約され、スクリプト処理がより効率的になることがわかります。

パイプラインのしくみ

オブジェクトを「パイプ」する、つまり、1 つのコマンドの出力に含まれるオブジェクトを別のコマンドに送信すると、Windows PowerShell はパイプされたオブジェクトを受信側のコマンドレットのパラメーターのいずれかに関連付けようとします。

そのために、入力オブジェクトをコマンドレット パラメーターに関連付ける、Windows PowerShell の「パラメーター バインド」コンポーネントは、次の条件を満たすパラメーターを見つけようとします。

-- パラメーターはパイプラインからの入力を受け入れる必要があります (すべてがそうとは限りません)。

-- パラメーターは、送信されるオブジェクトの型またはオブジェクトが変換できる型を受け入れる必要があります。

-- パラメーターはコマンドで既に使用されていることは許されません。

たとえば、Start-Service コマンドレットは、多くのパラメーターを持っていますが、そのうち Name と InputObject の 2 つのみがパイプライン入力を受け入れます。Name パラメーターは文字列を受け取り、InputObject パラメーターはサービス オブジェクトを受け取ります。したがって、文字列とサービス オブジェクト (および文字列とサービス オブジェクトに変換が可能なプロパティを持つオブジェクト) を Start-Service にパイプできます。

Windows PowerShell のパラメーター バインド コンポーネントがパイプされたオブジェクトを受信側コマンドレットのパラメーターに関連付けられない場合には、コマンドは失敗して、Windows PowerShell が不足しているパラメーター値の入力を求めます。

パイプされたオブジェクトを特定のパラメーターに関連付けるように、パラメーター バインド コンポーネントを強制することはできません。また、パラメーターを提案することもできません。その代わり、コンポーネントのロジックが、できるだけ効率的にパイプ処理を管理します。

一度に 1 つの処理

オブジェクトをコマンドにパイプすることは、コマンドのパラメーターを使用して、オブジェクトを送信するのとよく似ています。

たとえば、コンピューター上のサービスを表すオブジェクトを、

  get-service | format-table -property name, dependentservices

などの Format-Table コマンドにパイプすることは、サービス オブジェクトを変数に保存し、Format-Table の InputObject パラメーターを使用して、サービス オブジェクトを送信するのとよく似ています。

  $services = get-service
                  format-table -inputobject $services -property name, dependentservices

またはコマンドのパラメーター値への埋め込み

                  format-table -inputobject (get-service wmi) -property name, dependentservices

ただし、重要な違いがあります。複数のオブジェクトをコマンドレットにパイプすると、Windows PowerShell は、オブジェクトをコマンドレットに一度に 1 つずつ送信します。コマンド パラメーターを使用すると、オブジェクトは 1 つの配列オブジェクトとして送信されます。

この一見技術的な違いは、興味深く、時に有用な結果をもたらす場合があります。

たとえば、Get-Process コマンドレットからの複数のプロセス オブジェクトを Get-Member コマンドレットにパイプする場合、Windows PowerShell は各プロセス オブジェクトを一度に 1 つずつ Get-Member に送信します。Get-Member はプロセス オブジェクトの .NET クラス (型) とそのプロパティおよびメソッドを表示します。(Get-Member は重複を排除するため、オブジェクトがすべて同じ型の場合は、ただ 1 つのオブジェクト型が表示されます。)

この場合、Get-Member は、各プロセス オブジェクト、つまり System.Diagnostics.Process オブジェクトのプロパティとメソッドを表示します。

                 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 の InputObject パラメーターを使用する場合は、Get-Member が 1 つの単位として System.Diagnostics.Process オブジェクトの配列を受信し、オブジェクトの配列のプロパティを表示します。(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 つずつ送信されることに注意してください。

パイプライン入力の受け入れ

オブジェクトをパイプラインで受信するために、受信側のコマンドレットにはパイプライン入力を受け入れるためのパラメーターが必要です。Full または Parameter パラメーター付きで Get-Help コマンドを使用し、コマンドレットのどのパラメーター (存在する場合) がパイプライン入力を受け入れるかを判別できます。

Get-Help の既定の表示では、パラメーター属性のテーブルに「パイプライン入力の受け入れ」項目が表示されます。このテーブルは、Get-Help コマンドレットの Full または Parameter パラメーターを使用する場合にのみ表示されます。

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

        get-help start-service -full

        get-help start-service -parameter *

たとえば、Start-Service コマンドレットのヘルプは、Name および InputObject パラメーターがパイプライン入力を受け入れること (「true」) を示します。その他のすべてのパラメーターは、「パイプライン入力の受け入れ」行の値が「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?                    1
           Default value
      -->  Accept pipeline input?       true (ByValue, ByPropertyName)
           Accept wildcard characters?  true

        -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?                    false
           Position?                    named
           Default value
      -->  Accept pipeline input?       true (ByValue)
           Accept wildcard characters?  false

つまり、オブジェクト (PsObject) をパイプラインを通じて Where-Object コマンドレットに送信することができ、Windows PowerShell はオブジェクトを InputObject パラメーターに関連付けます。

パイプライン入力の受け入れ方法

コマンドレットのパラメーターは、次の 2 つの方法のいずれかでパイプライン入力を受け入れることができます。

-- ByValue:「値によって」入力を受け入れるパラメーターは、パラメーター値と同じ .NET 型を持つパイプ処理されたオブジェクトまたはその型に変換可能なオブジェクトを受け入れることができます。

たとえば、Start-Service の Name パラメーターは、値によってパイプライン入力を受け入れます。文字列オブジェクトまたは文字列に変換可能なオブジェクトを受け入れることができます。

-- ByPropertyName:「プロパティ名によって」入力を受け入れるパラメーターは、オブジェクトのプロパティがパラメーターと同じ名前を持つ場合にのみ、パイプされたオブジェクトを受け入れることができます。

たとえば、Start-Service の Name パラメーターは、Name プロパティを持つオブジェクトを受け入れることができます。

(オブジェクトのプロパティを一覧表示するには、Get-Member にパイプします。)

一部のパラメーターは、値またはプロパティ名によってオブジェクトを受け入れることができます。これらのパラメーターはパイプラインから入力を簡単に受け取るように設計されています。

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

パイプライン エラーのためにコマンドが失敗する場合、障害を調査して、コマンドを書き直すことができます。

たとえば、次のコマンドは Get-Item コマンドレットを使用して、転送先のパスを取得し、そのパスを Move-ItemProperty コマンドレットにパイプすることで、1 つのレジストリ キーから別のレジストリ キーにレジストリ エントリを移動しようとします。

具体的には、コマンドが Get-Item コマンドレットを使用して、転送先のパスを取得します。このコマンドは、パイプライン演算子を使用して、結果を Move-ItemProperty コマンドレットに送信します。Move-ItemProperty コマンドは、移動されるレジストリ エントリの現在のパスと名前を指定します。

          get-item -path hklm:\software\mycompany\sales | 
          move-itemproperty -path hklm:\software\mycompany\design -name product

このコマンドは失敗し、Windows PowerShell は次のエラー メッセージを表示します。

         Move-ItemProperty : The input object cannot be bound to any parameters for the
         command either because the command does not 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 product

調査をするには、Trace-Command コマンドレットを使用して、Windows PowerShell のパラメーター バインド コンポーネントをトレースします。次のコマンドは、コマンドの処理中に、パラメーター バインド コンポーネントをトレースします。-pshost パラメーターを使用して、結果をコンソールに表示し、-filepath コマンドを使用して、将来の参照用に結果を debug.txt ファイルに送信します。

         trace-command -name parameterbinding -expression {get-item -path hklm:\software\mycompany\sales |
             move-itemproperty -path hklm:\software\mycompany\design -name product} -pshost -filepath debug.txt

トレースの結果は長くなりますが、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]
        ...

最後に、パスを Move-ItemProperty の Destination パラメーターにバインドする試みが失敗したことを示します。

        ...
        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 パラメーターの属性を表示します。次のコマンドは、Destination パラメーターに関する詳細情報を取得します。

get-help move-itemproperty -parameter destination

結果は、Destination がパイプライン入力を「プロパティ名によって」のみ受け取ることを示します。つまり、パイプされたオブジェクトには、Destination という名前のプロパティが必要です。

        -destination <string>
            Specifies the path to the destination location.

            Required?                    true
            Position?                    2
            Default value
            Accept pipeline input?       true (ByPropertyName)
            Accept wildcard characters?  true    

Move-ItemProperty コマンドレットにパイプされるオブジェクトのプロパティを表示するには、Get-Member コマンドレットにパイプします。次のコマンドは、コマンドの最初の部分の結果を Get-Member コマンドレットにパイプします。

          get-item -path hklm:\software\mycompany\sales | get-member

出力は、項目が Destination プロパティを持たない Microsoft.Win32.RegistryKey であることを示しています。それでコマンドが失敗した理由の説明がつきます。

コマンドを修正するには、Move-ItemProperty コマンドレットで変換先を指定する必要があります。Get-ItemProperty コマンドを使用してパスを取得できますが、名前と変換先はコマンドの Move-ItemProperty 部分に指定する必要があります。

         get-item -path hklm:\software\mycompany\design | 
             move-itemproperty -dest hklm:\software\mycompany\design -name product    

コマンドが機能したことを確認するには、Get-ItemProperty コマンドを使用します。

get-itemproperty hklm:\software\mycompany\sales

結果は、Product レジストリ エントリが Sales キーに移動されたことを示します。

        PSPath       : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\software\mycompany\sales
        PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\software\mycompany
        PSChildName  : sales
        PSDrive      : HKLM
        PSProvider   : Microsoft.PowerShell.Core\Registry
        Product      : 18

関連項目

about_objects

about_parameters

about_command_syntax

about_foreach