しばたテックブログ

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

PowerShellのStandard output streamは標準出力から何を受け取っているのか?

前のエントリたちのさらに続き。

stknohg.hatenablog.jp

stknohg.hatenablog.jp

これらのエントリで、

外部のプロセスを呼び出し標準出力(StdOut)へ出力されたデータは最終的にString[]型に変換されてStandard output streamに出力される様です。

と言った内容を実際に確かめてみます。

検証プログラム

今回も簡単な検証プログラムOutput.exeを用意しました。

using System;
using System.Text;

namespace Output
{
    class Program
    {
        static void Main(string[] args)
        {                        
            // 標準出力に渡すバイト列を随時変えていく
            var Buffer = System.Text.Encoding.ASCII.GetBytes("Hello!");

            var StdOut = Console.OpenStandardOutput();
            StdOut.Write(Buffer, 0, Buffer.Length);
        }
    }
}

このプログラムはBufferに設定したバイト列を標準出力に書き出すだけのものになります。
本エントリではこのBufferの部分を随時変えたOutput.exeを用意して検証を進めていきます。

Standard output streamは標準出力から何を受け取っているのか?

まずは、Bufferの部分を

var Buffer = System.Text.Encoding.ASCII.GetBytes("Hello!");

としたASCIIコードでHello!(48 65 6C 6C 6F 21)を出力した結果を見てみます。

# [Console]::OutputEncoding = [Text.Encoding]::Default
# var Buffer = System.Text.Encoding.ASCII.GetBytes("Hello!");
PS C:\Temp> $Output = .\Output.exe
PS C:\Temp> $Output
Hello!
PS C:\Temp> $Output.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

外部プロセスの結果$OutputString型のHello!となりました。

ちなみに、前のエントリで使ったGetPipeline.exeを使って外部プロセス間でパイプラインを繋ぐとどんなデータが渡されるかを見てみると以下の図の様になります。

f:id:stknohg:20150628090126p:plain

上がコマンドプロンプト(Cmd.exe)で繋いだとき、下がPowerShellで繋いだ時になります。
PowerShellで繋ぐと一旦Standard output streamに乗るため改行コードCRLF(0D 0A)が付与されていることがわかります。

続けて、Bufferの部分を

var Buffer = System.Text.Encoding.Default.GetBytes("あ");

としたMS932のあ(82 A0)を出力した結果を見てみます。

# [Console]::OutputEncoding = [Text.Encoding]::Default
# var Buffer = System.Text.Encoding.Default.GetBytes("あ");
PS C:\Temp> $Output = .\Output.exe
PS C:\Temp> $Output
あ
PS C:\Temp> $Output.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

こちらの場合も特に問題なくString型のとなりました。

ここで[Console]::OutputEncoding = [Text.Encoding]::ASCIIに変更するとどうなるか見てみます。

# var Buffer = System.Text.Encoding.Default.GetBytes("あ");
PS C:\Temp> [Console]::OutputEncoding = [Text.Encoding]::ASCII
PS C:\Temp> $Output = .\Output.exe
PS C:\Temp> $Output
??
PS C:\Temp> $Output.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

となり、文字化けした状態の文字列??となってしました。
この後で[Console]::OutputEncoding = [Text.Encoding]::Defaultに戻しても、

PS C:\Temp> [Console]::OutputEncoding = [Text.Encoding]::Default
PS C:\Temp> $Output
??

となり、ではなく??としてStandard output streamに渡されてしまっていることがわかります。 この結果からStandard output stream[Console]::OutputEncodingでエンコードされた文字列を受け取っていることがわかります。

補足

ちなみに、適当なバイナリデータを渡しても[Console]::OutputEncodingでエンコードした結果の文字列を受け取ります。

また、改行ありの文字列を受け取るとString[]型(PowerShell上はObject[]扱いとなる)として受け取ります。

var Buffer = System.Text.Encoding.Default.GetBytes("1行目\n2行目");

で試してみると、

# [Console]::OutputEncoding = [Text.Encoding]::Default
# var Buffer = System.Text.Encoding.Default.GetBytes("1行目\n2行目");
PS C:\Temp> $Output = .\Output.exe
PS C:\Temp> $Output
1行目
2行目
PS C:\Temp> $Output.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array


PS C:\Temp> $Output[0].GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object


PS C:\Temp> $Output[1].GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

といった結果になります。