前のエントリたちのさらに続き。これで最後です。
最後に標準エラー出力
からError output stream
に対してどういったデータが流れているか確認してみます。
検証プログラム
今回も検証プログラムOutput2.exe
を用意します。
using System; using System.Text; namespace Output2 { class Program { static void Main(string[] args) { // 標準エラー出力に渡すバイト列を随時変えていく var Buffer = System.Text.Encoding.ASCII.GetBytes("Error!"); var StdErr = Console.OpenStandardError(); StdErr.Write(Buffer, 0, Buffer.Length); } } }
基本的に前回の検証プログラムOutput.exe
と同様で、Buffer
に設定したバイト列を標準エラー出力に書き出します。
本エントリではこのBufferの部分を随時変えたOutput2.exe
を用意して検証を進めていきます。
検証の前に
今回は標準エラー出力、Error output streamにデータを流すのでPowerShellから取得するに一工夫する必要があります。
単純に$Output2 = .\Output2.exe
としても下の図の様になってしまい$Output2
は$null
になってしまいます。
そこで$Output2 = .\Output2.exe 2>&1
の様に2>&1
してやると$Output2
に値を設定することができます。
これをふまえて次に進んでいきます。
PowerShellのError output streamは標準エラー出力から何を受け取っているのか?
まずは、Buffer
の部分を
var Buffer = System.Text.Encoding.ASCII.GetBytes("Error!");
としたASCIIコードでError!(45 72 72 6F 72 21)
を出力した結果を見てみます。
# [Console]::OutputEncoding = [Text.Encoding]::Default # var Buffer = System.Text.Encoding.ASCII.GetBytes("Hello!"); PS C:\Temp> $Output2 = .\Output2.exe 2>&1 PS C:\Temp> $Output2 .\Output2.exe : Error! 発生場所 行:1 文字:12 + $Output2 = .\Output2.exe 2>&1 + ~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (Error!:String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError PS C:\Temp> $Output2.GetType() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True ErrorRecord System.Object PS C:\Temp> $Output2.TargetObject Error! PS C:\Temp> $Output2.Exception.Message Error!
今回はError output stream
にデータが渡されるので最終的なデータの型がErrorRecord
型になっています。
標準エラー出力に出力した部分はTargetObject
プロパティや、Exception.Message
プロパティに設定される様です。
前回同様にGetPipeline.exe
を使って外部プロセス間でパイプラインを繋ぐとどうなるかというと、
となりかなり差が出てしまっています。
これは単純に.\Output2.exe 2>&1
を実行してコンソールに出力される
.\Output2.exe : Error! 発生場所 行:1 文字:1 + .\Output2.exe 2>&1 + ~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (Error!:String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
の部分がパイプラインで渡されているためです。
続けて、Buffer
の部分を
var Buffer = System.Text.Encoding.Default.GetBytes("あ");
としたMS932のあ(82 A0)
を出力した結果を見てみます。
最初に[Console]::OutputEncoding = [Text.Encoding]::Default
の場合、
# [Console]::OutputEncoding = [Text.Encoding]::Default # var Buffer = System.Text.Encoding.Default.GetBytes("あ"); PS C:\Temp> $Output2 = .\Output2.exe 2>&1 PS C:\Temp> $Output2 .\Output2.exe : あ 発生場所 行:1 文字:12 + $Output2 = .\Output2.exe 2>&1 + ~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (あ:String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError PS C:\Temp> $Output2.TargetObject あ PS C:\Temp> $Output2.Exception.Message あ
次に[Console]::OutputEncoding = [Text.Encoding]::ASCII
の場合、
# var Buffer = System.Text.Encoding.Default.GetBytes("あ"); PS C:\Temp> [Console]::OutputEncoding = [Text.Encoding]::ASCII PS C:\Temp> $Output2 = .\Output2.exe 2>&1 PS C:\Temp> $Output2 .\Output2.exe : ?? ???? ?:1 ??:12 + $Output2 = .\Output2.exe 2>&1 + ~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (??:String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError PS C:\Temp> $Output2.TargetObject ?? PS C:\Temp> $Output2.Exception.Message ??
となり、標準出力→Standard output streamの時と同様に標準エラー出力→Error output streamにデータを渡す際も[Console]::OutputEncoding
でエンコードされた文字列にしている様です。
補足
前回同様、適当なバイナリデータを渡しても[Console]::OutputEncoding
でエンコードした文字列扱いとなります。
改行ありのデータ
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> $Output2 = .\Output2.exe 2>&1 PS C:\Temp> $Output2 .\Output2.exe : 1行目 発生場所 行:1 文字:12 + $Output2 = .\Output2.exe 2>&1 + ~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (1行目:String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError 2行目 PS C:\Temp> $Output2.Count 2 PS C:\Temp> $Output2.GetType() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array PS C:\Temp> $Output2[0].GetType() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True ErrorRecord System.Object PS C:\Temp> $Output2[1].GetType() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True ErrorRecord System.Object
となりErrorRecord[]
が渡されていることがわかります。