以前に書いた
に対するさらなる補足です。
本当はもう少し早い時期に書きたかったのですがなかなか書けずにおりました...
別にWrite-Outputを使わなくても...
先のエントリで触れた様にWrite-Output
は
説明
Write-Output は、"出力ストリーム" や "正常終了パイプライン" とも呼ばれるプライマリ パイプラインにオブジェクトを送信します。
と、オブジェクトを"出力ストリーム"(1>)に送るだけのコマンドレットです。
しかしながら、PowerShellでは別にWrite-Output
を使わずとも変数やリテラルを定義・評価するだけでそのオブジェクトは"出力ストリーム"(1>)に送られます。
極端な例を出すとコンソールに"Hello World!"
と打つだけで文字列は評価されてストリームに乗り、最終的には以下の様にコンソールに文字列が表示されることになります。
# PowerShellではWrite-Outputを使わずとも評価されるだけでそのオブジェクトは出力ストリーム(1>)に乗り、 # 最終的にコンソールに表示される。 PS C:\> "Hello World!" ↓ PS C:\> "Hello World!" | Out-Default ↓ PS C:\> "Hello World!" | Out-Host Hello World!
ですのでWrite-Output
を使わなくても同様のことは普通にできてしまいます。
Write-Outputは何のために存在しているのか?
ではWrite-Output
は何のために存在しているのでしょうか?
いろいろ調べてみた結果、いくつかの候補は思いついたのですが、これだという結論にたどり着くことができませんでした。
以下その候補について書いていきます。
1. ストリームへの出力を統一するためのWrite-Output
上のエントリでも述べていますがPowerShellのストリームは出力ストリーム(1>)~Information Stream(5>)とProgress Streamがあり、それぞれのストリームにオブジェクトを出力するために、
- Write-Output
- Write-Error
- Write-Warning
- Write-Verbose
- Write-Debug
- Wirte-Information
- Write-Progress
コマンドレットが存在しています。
各ストリームへの出力方法の統一のためにWrite-Output
が存在しているというのが一番合理的な説明ができる気がします。
個人的には、初めにWrite-Output
があり、利便性のために出力ストリーム(1>)への出力だけはWrite-Output
が無くても良い様にしたのではないかと予想しています。
これが一番の理由なのではないかと思っているのですが明確な根拠を得ることはできませんでした...
あくまで予想にすぎませんのでその点はご注意ください。
2. echoコマンドとしてのWrite-Output
PowerShellでは既定でecho
がWrite-Output
のエイリアスとして定義されています。
以下のコマンドでエイリアスの実体を確認することができます。
PS C:\> Get-Alias echo CommandType Name Version Source ----------- ---- ------- ------ Alias echo -> Write-Output
コマンドプロンプト等の他のシェルの標準出力に対応するのが出力ストリームであるためWrite-Host
でなくWrite-Output
がエイリアスの実体になるのは妥当だと思います。
ちょっと逆説的ですが、PowerShellにおけるecho
コマンドの役割を果たすためにWrite-Output
が存在していると言えなくもありません。
こちらも予想に過ぎず根拠もかなり弱い感じです...
3. -NoEnumerateのためのWrite-Output
これは存在理由というよりはWrite-Output
のもう一つの役割と言ったほうが正確ですね。
PowerShellのパイプラインは配列等の"Enumerable"なオブジェクトが渡された場合、その要素ごとに順次パイプラインを流れていく挙動となります。
例えば以下の様に@(1..100)
の配列をパイプラインでつなげると、後続のForEach-Object
には配列の各要素のint型の値が順に渡され、Processブロックは100回呼び出されることになります。
PS C:\> Write-Output @(1..100) | ForEach-Object -Begin { $Counter = 0 } -Process { $Counter += 1 } -End { Write-Host "$Counter 回呼ばれました!" } 100 回呼ばれました!
Write-Output
には-NoEnumerate
というパラメーターが存在しており、このパラメーターを指定した場合はパイプラインに要素ごとではなく配列そのものを渡す挙動になります。
先程の例に対し-NoEnumerate
を付けてパイプラインでつなげると、後続のForEach-Object
にはint[]型の配列がそのまま渡され、Processブロックの呼び出しは1回だけになります。
PS C:\> Write-Output @(1..100) -NoEnumerate | ForEach-Object -Begin { $Counter = 0 } -Process { $Counter += 1 } -End { Write-Host "$Counter 回呼ばれました!" } 1 回呼ばれました!
(追記あり)Write-Output
を使わない場合は-NoEnumerate
と同じ挙動をさせることはできません。
このためにWrite-Output
が存在しているというのは...流石に言い過ぎですね...
【2016/03/26追記】
カンマ単項演算子のことを完全に失念していました。
これを使えばWrite-Output
を使わなくとも-NoEnumerate
と同じ挙動をさせることができますね...Orz
PS C:\> ,@(1..100) | ForEach-Object -Begin { $Counter = 0 } -Process { $Counter += 1 } -End { Write-Host "$Counter 回呼ばれました!" } 1 回呼ばれました!
言い過ぎどころかただの間違いでしたね。まあ、Write-Output
にはこういうパラメーターもあるよという事でお許しください。
Write-Outputを明示して使うべきか?
結局Write-Output
は何のために存在しているかという問いには答えることは出来なかったのですが、答えがわからなくても実害は無いので大きな問題にはなりません(
あまり深く考えないほうが幸せなPowerShellライフを過ごせるんじゃないかと思います。
ただ、存在理由がぼんやりしているせいで、PowerShellを利用する際にWrite-Output
明示して使うべきかどうかという点は非常に悩ましいです。
-NoEnumrate
を使う場合はWrite-Outut
を明示しなければなりませんが、それ以外の場合については公式にガイドラインがあるわけでもないため、正直なところ、答えのでない問いだと思っています。
私個人の思いとしてはWrite-Output
は可能な限り明示した方が良いと考えています。
これは、PowerShellにある程度慣れていないとWrite-Output
を明示しない場合でストリームに出力されるコードとされないコード(例えば値の代入は評価した結果を出すわけではないのでストリームには出力されません)を区別するのは難しいと思うからです。
ちょっと極端な例ですが、例えば以下の様なコードがあった場合、PowerShellに慣れていないと何が出力されるかすぐにはわからないと思います。
$i = 0 ++$i (++$i)
これに対してWrite-Output
を明示してやるとかなりわかりやすくなると思います。
$i = 0 ++$i Write-Output (++$i)
最終的にどちらが良いかの判断はみなさんに委ねます。