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-List
Format-Table
した結果が出力されます。
このためオブジェクトを標準入力(StdIn)
に渡すとFormat-List
Format-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追記]
補足的なエントリ、
を書きましたのでよろしければこちらもご覧ください。
【改訂新版】 Windows PowerShell ポケットリファレンス
- 作者: 牟田口大介
- 出版社/メーカー: 技術評論社
- 発売日: 2013/02/23
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (3件) を見る
*1:PowerShell3.0以上