しばたテックブログ

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

Write-HostとWrite-Outputの表示の違い

前回のエントリの補足です。

stknohg.hatenablog.jp

前回はWrite-HostWrite-Outputの違いについて触れ、全くの別物であることを説明しました。

別物であるとはいえ、実際の利用においては両方ともコンソールに文字列を表示する用途で同様に扱うことが多いと思います。
そこで本エントリではコンソールに文字列を表示する場合の両者の違いについて説明していきます。

Write-Host、Write-Outputの入力パラメータについて

本題に入る前に、Write-HostWrite-Outputの入力パラメータについて軽く触れます。
Write-Host-ObjectWrite-Output-InputObjectは名前付き引数にも関わらず複数の入力を受け付けることができ可変長引数の様に振る舞います。

# Write-Hostの入力パラメータ -Object が3つの引数を受け取り可変長の様に振る舞っている
PS C:\> Write-Host 123 456 789
123 456 789

# Write-Outputの -InputObject パラメーターも同様
PS C:\> Write-Output 123 456 789
123
456
789

一見不可解な動作ですが、これはWrite-Host-ObjectWrite-Output-InputObjectパラメータにValueFromRemainingArguments属性が設定されているためです。

ValueFromRemainingArguments属性についてはabout_Functions_Advanced_Parametersに詳細が記載されており、

ValueFromRemainingArguments 引数は、パラメーターが、関数の他のパラメーターに割り当てられていないコマンド内のすべてのパラメーター値を受け取ることを示します。

との説明がなされています。

これは、例えば3つのパラメーターを持つ関数を5つの引数で呼び出した場合2つの引数が余りますが、ValueFromRemainingArguments属性が付いているパラメーターにこの余りをまとめてリストとして渡されます。
言葉で説明するより以下の例を見た方がわかりやすいと思います。

ValueFromRemainingArguments属性が付いた-Input2を持ち、3つの引数をとる簡単なファンクションFunc1を定義します。

function Func1
{
    [CmdletBinding()]
    param
    (
        $Input1,
        [Parameter(ValueFromRemainingArguments=$true)]
        $Input2,
        $Input3
    )
    Write-Host "-Input1 = $Input1(Type=$($Input1.GetType()))."
    Write-Host "-Input2 = $Input2(Type=$($Input2.GetType()))."
    Write-Host "-Input3 = $Input3(Type=$($Input3.GetType()))."
}

このFunc1に対して3つ以上の引数を指定すると以下の様になり、-Input2に余りの引数がリストの形でまとめられているのがわかります。
この例の様にパラメーター名を指定しない場合は後ろの引数がまとめられます。
(-Input22 4 5でなく3 4 5なのに注意してください。)

PS C:\> Func1 1 2 3 4 5
-Input1 = 1(Type=int).
-Input2 = 3 4 5(Type=System.Collections.Generic.List[System.Object]).
-Input3 = 2(Type=int).

パラメーター名を指定した場合は以下の様になります。

# パラメータ名で割り当てられなかった余りがValueFromRemainingArgumentsを持つパラメータにまとめられる
PS C:\> Func1 1 -Input3 2 -Input1 3 4 5
-Input1 = 3(Type=int).
-Input2 = 1 4 5(Type=System.Collections.Generic.List[System.Object]).
-Input3 = 2(Type=int).

# ValueFromRemainingArgumentsを持つパラメータを名前付きで指定するとパラメータの数が合わずエラーとなる
PS C:\> Func1 1 -Input2 2 3 4 5
Func1 : 引数 '4' を受け入れる位置指定パラメーターが見つかりません。
発生場所 行:1 文字:1
+ Func1 1 -Input2 2 3 4 5
+ ~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Func1]、ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Func1

Write-HostとWrite-Outputの表示の違い

ここから本題に入りWrite-HostWrite-Outputの表示の違いを説明します。

色指定の有無

まずは基本から。
Write-Hostでは-ForegroundColorにより文字の前景色、-BackgroundColorにより背景色を指定することができます。

前回のエントリで説明したとおり、Write-Outputは出力ストリームにオブジェクトを出力するためのものですので色を付けることはできません。

f:id:stknohg:20160117083906p:plain

改行の有無

Write-Hostでは-NoNewlineにより文字列表示後に改行をつけるか否かを設定できます。
-NoNewlineはSwitchパラメーターで既定値は$trueです。

f:id:stknohg:20160117084209p:plain

Write-Outputにはこのパラメータは無く、常に改行付きの文字列がコンソールに表示されます。

f:id:stknohg:20160117084221p:plain

オブジェクトの種類による違い

入力に文字列以外のオブジェクトを指定した場合、Write-Hostでは入力オブジェクトを無条件にToString()した結果が表示されます。

f:id:stknohg:20160117084713p:plain

Write-OutputではPowerShellの書式設定に応じた表示となります。
こちらも前回説明した通り、Write-Outputしたオブジェクトは最終的にFormat-*(Format-Default)が呼ばれて書式設定されるためになります。

f:id:stknohg:20160117084723p:plain

可変長引数を受けた場合の表示の違い

最初に説明したとおり、Write-HostWrite-Outputでは可変長の引数を受けることができます。

Write-Hostでは入力値を横並びに表示します。
-Separatorパラメーターで横並びにする際の区切り文字を指定することもできます。

f:id:stknohg:20160117090139p:plain

Write-Outputでは入力値を配列にして返します。このため最終的なコンソールへの表示は縦並びとなります。

f:id:stknohg:20160117090154p:plain

f:id:stknohg:20160117090201p:plain

配列を指定した場合の違い

引数に配列を指定した場合は可変長引数を受けた場合と同様の結果となります。

f:id:stknohg:20160117090416p:plain

ハッシュテーブルを指定した場合の違い

引数にハッシュテーブルを指定した場合、Write-Hostはオブジェクトの配列を受けた扱いとなり"System.Collections.DictionaryEntry"の文字列が横並びで表示されます。
Write-Outputではキーと値の内容が表示されます。

f:id:stknohg:20160117090712p:plain

補足

一番利用頻度が高いであろう文字列の出力についての詳細はぎたぱそ先生の以下のエントリが非常に有用ですので参考にしてください。

tech.guitarrapc.com