しばたテックブログ

気分で書いている技術ブログです。

PowerShellのパイプラインは外部プロセスに何を渡しているのか?

前のエントリの続き的な。

stknohg.hatenablog.jp

PowerShellが外部のプロセスにパイプラインでデータを渡す際は、

コンソールに出力される文字列を$OutputEncodingに設定されているエンコーディングでエンコードした文字列を渡している

・・・(中略)・・・

また、文字列でないオブジェクトについてはコンソールにはFormat-Tableした結果が出力されます。
このためオブジェクトを標準入力(StdIn)に渡すとFormat-Tableした結果の文字列が渡されることになります。

と言いましたが、実際どんなふうになっているかを確かめてみるのが今回のエントリになります。
特に環境依存な部分はないと思いますがWindows 8.1上で確認しています。

検証プログラム

検証用に簡単なプログラムGetPipeline.exeを用意しました。
これは単純に標準入力に受け取ったデータを16進表示するだけのプログラムです。

using System;

namespace GetPipeline
{
    class Program
    {
        static void Main(string[] args)
        {
            var StdIn = Console.OpenStandardInput();
            var Buffer = new byte[1024];
            var Reads = 0;
            while ((Reads = StdIn.Read(Buffer, 0, Buffer.Length)) > 0)
            {
                for (int i = 0; i < Reads ; i++)
                {
                    Console.Write(Buffer[i].ToString("X2"));
                    Console.Write(' ');       
                }
            }
            Console.WriteLine();
        }
    }
}

文字列を渡した場合

最初にデフォルトの$OutputEncoding=[Text.Encoding]::ASCIIの状態で適当な文字列Hello!をパイプラインで渡してみます。

# $OutputEncoding=[Text.Encoding]::ASCII
PS C:\Temp> "Hello!" | .\GetPipeline.exe
48 65 6C 6C 6F 21 0D 0A

Hello!(48 65 6C 6C 6F 21)の後にCRLF(0D 0A)がついていることがわかります。

次にひらがなでを渡してみます。

# $OutputEncoding=[Text.Encoding]::ASCII
PS C:\Temp> "あ" | .\GetPipeline.exe
3F 0D 0A

ASCIIコードではは表現できないため文字化けした結果?(3F)CRLF(0D 0A)が渡されていることがわかります。

続けて$OutputEncoding=[Text.Encoding]::Default (MS932)するとどうなるか確かめてみます。

PS C:\Temp> $OutputEncoding = [Text.Encoding]::Default
PS C:\Temp> "あ" | .\GetPipeline.exe
82 A0 0D 0A

すると?に化けることなくあ(82 A0)として渡される様になりました。

文字列以外を渡した場合

文字列以外のデータを渡した場合どうなるかを見ていきます。

数値の場合

# $OutputEncoding=[Text.Encoding]::ASCII
PS C:\Temp> [int]123 | .\GetPipeline.exe
31 32 33 0D 0A

PS C:\Temp> [Double]12.3 | .\GetPipeline.exe
31 32 2E 33 0D 0A

PS C:\Temp> 10 / 3 | .\GetPipeline.exe
33 2E 33 33 33 33 33 33 33 33 33 33 33 33 33 33 0D 0A
# 10 / 3 の計算結果は 3.33333333333333 とコンソールに表示される。

数値を文字列にして渡しています。

Bool型の場合

# $OutputEncoding=[Text.Encoding]::ASCII
PS C:\Temp> $true | .\GetPipeline.exe
54 72 75 65 0D 0A

PS C:\Temp> $false | .\GetPipeline.exe
46 61 6C 73 65 0D 0A

TrueおよびFalseの文字列が渡されます。コンソールに表示される内容と同じです。

NULLの場合

# $OutputEncoding=[Text.Encoding]::ASCII
PS C:\Temp> $null | .\GetPipeline.exe

この場合は何も渡されません。

その他の型の場合

最後にls(Get-ChildItem)コマンドの結果で試してみます。

今回の例ではlsを実行すると以下の様な結果になります。

PS C:\Temp> ls


    ディレクトリ: C:\Temp


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2015/06/27     20:15       5120 GetPipeline.exe

コンソールには上の様に表示され、コマンドの実行結果は、今回は1ファイルのみが取得されているのでSystem.IO.FileInfoクラスのオブジェクトが返されています。
これをふまえてパイプラインで渡した結果を確認してみます。

# コンソールの表示結果に日本語があるため$OutputEncodingを変えている
PS C:\Temp> $OutputEncoding = [Text.Encoding]::Default
PS C:\Temp> ls | .\GetPipeline.exe
0D 0A 0D 0A 20 20 20 20 83 66 83 42 83 8C 83 4E 83 67 83 8A 3A 20 43 3A 5C 54 65 6D 70 0D 0A 0D 0A 0D 0A 4D 6F 64 65 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 4C 61 73 74 57 72 69 74 65 54 69 6D 65 20 20 20 20 20 4C 65 6E 67 74 68 20
4E 61 6D 65 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0D 0A 2D 2D 2D 2D
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 20 20 20 20 20 2D 2D 2D 2D 2D 2D
20 2D 2D 2D 2D 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0D 0A 2D 61 2D
2D 2D 20 20 20 20 20 20 20 20 32 30 31 35 2F 30 36 2F 32 37 20 20 20 20 20 32 30 3A 31 35 20 20 20 20 20 20 20 35 31 32
30 20 47 65 74 50 69 70 65 6C 69 6E 65 2E 65 78 65 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0D 0A 0D 0A

これ、ちょっと長ったらしいのですが、コンソールに帰ってきた文字列


    ディレクトリ: C:\Temp


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2015/06/27     20:15       5120 GetPipeline.exe

の部分を返しています。

おまけ

ちなみに、コンソールに表示される文字列が渡されるということでWrite-Hostした結果をパイプラインで渡すとどうなるかというと、

f:id:stknohg:20150627221609p:plain

こんな感じでコンソールに文字は出力されますが外部プロセスには何も渡されません。
まあ、当然の動作ではありますが一応確認ということで。

とりあえずこんな感じでした。