PowerShellの基本的なところがわかってなかったシリーズ第二弾です。
一部わからないままの部分がありますのでご存知の方いらっしゃいましたらご指摘ください。
最初にまとめ
今回のエントリの内容をまとめて図に表すと以下の様な感じになります。

ちょっとごちゃごちゃしてますが、この図を踏まえて以下の内容をご覧ください。
PowerShellのストリームとリダイレクト、パイプライン
PowerShellのストリームとリダイレクト
基本的にはこのエントリやabout_Redirectionのはなしになります。
PowerShellではデータの入出力はストリームを介して行われます。
標準出力に相当するStandard output streamと標準エラー出力に相当するError output streamがあり、Standard output streamは通常のファンクションやコマンドレットの実行結果およびWrite-Outputの結果などが出力され、Error output streamはTrapした例外やWrite-Errorの結果が出力されます。 そしてStandard output streamは1>で、Error output streamは2>でリダイレクトすることができます。
また、PowerShell 3.0からWrite-Warningの結果が出力されるWarning output stream(3>)、Write-Verboseの結果が出力されるVerbose output stream(4>)、Write-Debugの結果が出力されるDebug output stream(5>)が追加され、全部で5つのストリームが存在します。 ています。
---- 2015/06/29追記 ----
上記ストリームに加え、Write-Progressの結果が出力されるストリーム(こちらはPowerShellの言語仕様書に名称が明記されていなかったのでProgress streamとしておきます)と、標準入力に相当するストリーム(こちらはInput Streamとしておきます)の計7つのストリームが存在します。
ちなみにProgress Streamはリダイレクトすることができません。
---- 追記ここまで ----
---- 2015/07/05追記 ----
さらに、PowerShell 5.0から新たにInforation Stream(6>)が増え、これまでストリームに乗ることのなかったWrite-Hostの結果をストリームに乗せることができる様になります。
---- 追記ここまで ----
Input Streamを除いた分をまとめると以下の様になります。
| ストリーム | 出力されるオブジェクトの型 | リダイレクト方法 | 出力方法 | 特記事項 |
|---|---|---|---|---|
| Standard output stream | Objectおよびその派生型 (正確にはPSObjectらしい) | >(>>) 1>(1>>) | Write-Outputなど | 標準出力に相当 |
| Error output stream | ErrorRecord | 2>(2>>) | Write-Errorなど | 標準エラー出力に相当 |
| Warning output stream | WarningRecord | 3>(3>>) | Write-Warningのみ | PowerShell 3.0以上 |
| Verbose output stream | VerboseRecord | 4>(4>>) | Write-Verboseのみ | PowerShell 3.0以上 |
| Debug output stream | DebugRecord | 5>(5>>) | Write-Debugのみ | PowerShell 3.0以上 |
| Progress stream | ProgressRecord | リダイレクト不可 | Write-Progressのみ | |
| Information stream | InformationRecord | 6>(6>>) | Write-Host、Write-Informationのみ | PowerShell 5.0以上 |
補足として、すべてのストリームに対してリダイレクトする場合は*>(*>>)を記述することもできます。*1
そしてStandard output Stream以外のストリームは>&1とすることでStandard output Streamへさらにリダイレクトすることができます。
PowerShellのストリームとパイプライン
PowerShellのパイプラインはStandard output Streamでのみ可能であり、その他のストリームではパイプラインを繋げることはできません。
Write-Hostについて
Write-Hostの結果は上記のどのストリームにも乗らずただコンソール画面に表示されて終わりとなります。
Microsoft Connect is Retired - Collaborate | Microsoft Docs では当初Host Stream(6>)が提案されていた様
ですが採用には至っていません。
ただ、現時点で6>は予約済みとなっておりいずれは導入される可能性があるのかもしれません。
---- 2015/07/05追記 ----
PowerShell 5.0からInformation stream(6>)が増え、Write-Hostの結果や新設のWrite-Informationの結果を乗せることができる様になります。
詳細については、
Weekend Scripter: Welcome to the PowerShell Information Stream – Hey, Scripting Guy! Blog
で確認できます。
---- 追記ここまで ----
PowerShellのストリームと標準入出力
ここまでは純粋にPowerShell内部のはなしになるのですが、ここからはPowerShellから外部のプロセス(.exe)を呼び出した場合について触れていきます。
PowerShell外部のプロセスにおいて入出力は標準入力(StdIn)、標準出力(StdOut)、標準エラー出力(StdErr)を介して行われます。
PowerShellではないのでデータに型はなく生のバイナリ、またはテキストデータが流れます。
当然これらの標準入出力とPowerShellのストリームは似て非なるものであり、PowerShellから外部のプロセスを呼び出した場合は相互にデータの変換を行う必要があります。
ここでPowerShellの仕様書などいろいろ調べてみたのですが、正直どういった変換が行われているのかわかりませんでした。
このため以下には自分で調べてわかった範囲のことを書いていきます。
内容に誤りがあるかもしれませんがその際はご指摘いただければ訂正します。
標準出力(StdOut)からStandard output streamへの変換について
外部のプロセスを呼び出し標準出力(StdOut)へ出力されたデータは最終的にString[]型に変換されてStandard output streamに出力される様です。
バイナリデータを出力した際も文字化けした状態のString[]になりました。
---- 2015/06/26追記 ----
データは[Console]::OutputEncodingで設定されているエンコーディングでエンコードされる様です。
[Console]::OutputEncodingはコンソールのコードページとも関わっているので変更するとコンソールで使えるフォントが変わってきます。
フォントにない文字の場合は文字化けしてStandard output streamに渡されます。
---- 追記ここまで ----
標準エラー出力(StdErr)からError output streamへの変換について
標準エラー出力(StdErr)出力されたデータは最終的にErrorRecord[]型に変換されてError output streamに出力されます。
変換ロジックについては検証できなかったため不明です。
---- 2015/06/28追記 ----
変換ロジックは標準出力(StdOut)からStandard output streamへの変換と同様です。
---- 追記ここまで ----
PowerShellから標準入力(StdIn)へのパイプラインについて
PowerShellのパイプライン演算子は外部のプロセスに対しても適用することができます。
"Hello!" | Hoge.exe
といったコードを書くことができ、Standard output streamから標準入力(StdIn)を経由して外部のプロセスにデータを渡せます。
ただ、Standard output streamには型付きのあらゆるオブジェクトが出力されます。
標準入力(StdIn)に渡すためにはなんらかの変換をする必要があるわけですが、私の検証した限りでは改行付きの文字列に変換したテキストデータを渡している様に見受けられました。
上記の例だとHello! + CrLfが渡される様です。
またこのテキストデータという部分について、ASCIIのみ受け付けるせいなのかコードページによるものなのか分からないのですがShift-JISの文字列を渡すと文字化けし、化けた後の"?"が外部のプロセスに渡されるといったこともありました。
---- 2015/06/26追記 ----
さらに調べてた結果、標準入力(StdIn)にはコンソールに出力される文字列を$OutputEncodingに設定されているエンコーディングでエンコードした文字列を渡している様です。
このため、通常の文字列の場合は改行コードが追加され、$OutputEncodingはデフォルトでASCIIであるためShift-JISの文字列が文字化けするといったことが起きていました。
$OutputEncodingを適宜変えてやることで文字化けせずにStandard output streamにデータを渡すことができます。
また、文字列でないオブジェクトについてはコンソールにはFormat-ListFormat-Tableした結果が出力されます。
このためオブジェクトを標準入力(StdIn)に渡すとFormat-ListFormat-Tableした結果の文字列が渡されることになります。
---- 追記ここまで ----
さらに外部のプロセスに対してパイプラインを渡すのはPowerShell で 外部コマンドをパイプで渡す時の問題について - tech.guitarrapc.cómにもある様に性能面でも問題を抱えています。
挙動の謎さと性能面の問題があることからPowerShellから外部のプロセスに対してパイプラインを渡すのはやめておいた方が賢明だと思います。
最後に
PowerShellのストリームと標準入出力の関係ついてわかる範囲で調べてみましたが、不明な点がまだまだありましたので、新しいことがわかり次第このエントリの内容は更新していきたいと思います。
ちなみにPowerShellの言語仕様書は以下からダウンロードするすることができます。
補足
[2015/06/26追記]
PowerShellにおける"ストリーム"という用語について、この用語が使われる様になったのはPowerShell 3.0からの様です。
PowerShell 2.0の言語仕様書を見るとStandard output stream、Error output streamはそれぞれStandard output、Error outputとだけ表記されていました。
単純に標準出力、標準エラー出力と対になるものとして認識されていたという事なのでしょうか?そのへんの事情はよくわかりません。
[2015/06/28追記]
補足的なエントリ、
を書きましたのでよろしければこちらもご覧ください。
- 作者: 牟田口大介
- 出版社/メーカー: 技術評論社
- 発売日: 2013/02/23
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (3件) を見る
*1:PowerShell3.0以上