Write-Host コマンドレットを無害化する

#概要
Write-Host コマンドレットの便利な機能を損なわずに出力の制御を可能とすることができるか試してみました。具体的には、標準のWrite-Host を、独自定義の(出力を制御可能な) Write-Host で置き換え、正常に動作するか確かめました[^1]（お遊びです）。

[^1]: この種の関数は、プロキシ関数（proxy function）と呼ばれます。パイプ経由の入力に対する動作まで同等にするには Steppable Pipeline というものを利用する必要があるようです。

#背景
Jeffrey Snover氏のブログ["Write-Host Considered Harmful"](http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/)(「Write-Hostは有害だ」)の影響で Write-Host コマンドレットは肩身の狭い存在になっています。有害とされる理由は、「(それを用いたスクリプトの)利用者にその出力を制御する術がないから※」ということです。
　確かに汎用的な(部品となるような)スクリプトを書く場合は Write-Host は有害であることが多いかもしれませんが、(独立したアプリケーションまたはそれに近いスクリプトを書く場合など)用途によっては実害なく便利に使えるので個人的にはそれほど目の敵にする必要はないと思っています。文字色を指定したり、改行を抑制したりといった機能は他の Write-* コマンドレットには用意されていません。

※Version5.0より前の話

#Write-Host の置き換え
以下の関数で標準の Write-Hostを置き換えると、\$Quiet 変数を介して外部から動作(出力のON/OFF)を制御できるようになります。Write-Verbose と同等のレベルで無害と言えるのではないでしょうか。さらに少し手を加えればファイルにリダイレクトさせることもできますね。

```powershell:
# by earthdiver1

Function Write-Host() {
    param( 
        [Parameter(Position=0, ValueFromPipeline=$True, ValueFromRemainingArguments=$True)][Object] $Object, 
        [ConsoleColor] $BackgroundColor = $Host.UI.RawUI.BackgroundColor,
        [ConsoleColor] $ForegroundColor = $Host.UI.RawUI.ForegroundColor,
        [Switch] $NoNewLine, 
        [Object] $Separator = " "
    )
    if (-not $Quiet) {
        Microsoft.PowerShell.Utility\Write-Host $Object -BackgroundColor $BackgroundColor -ForegroundColor $ForegroundColor -NoNewLine:$NoNewLine -Separator $Separator 
    }
}
```

　PowerShellのバージョンが5以上の場合は、Write-Informationを利用した以下の関数に置き換えて $InformationPreference で出力を制御することが可能です（こんなことしなくても6番目のストリームのリダイレクトで事足りるので無用ですが）。

- -NoNewline オプションの動作も模倣します。ただし、全角文字が混在する文字列がウィンドウの右端で折り返しになる場合に水平位置が少しずれることがあります。
- -Separator オプションは無視されます。
- 別途[投稿](https://qiita.com/earthdiver1/items/a3d8c70a3adf2d7df688)している Get-StringWidth 関数を使用しています。

```powershell:
# by earthdiver1
$InformationPreference = "Continue"

Function Write-Host() {
    param( 
        [Parameter(Position=0, ValueFromPipeline=$True, ValueFromRemainingArguments=$True)][Object] $Object,
        [ConsoleColor] $BackgroundColor,
        [ConsoleColor] $ForegroundColor,
        [Switch] $NoNewLine, 
        [Object] $Separator 
    )
    $String = [String]$Object
    if ($ForegroundColor) {
        $orgForegroundColor = $Host.UI.RawUI.ForegroundColor
        $Host.UI.RawUI.ForegroundColor = $ForegroundColor
    }
    if ($BackgroundColor) {
        $orgBackgroundColor = $Host.UI.RawUI.BackgroundColor
        $Host.UI.RawUI.BackgroundColor = $BackgroundColor
    }
    if ($NoNewLine) {
        $w = Get-StringWidth $String
        $x = ($Host.UI.RawUI.CursorPosition.X + $w) % $Host.UI.RawUI.BufferSize.Width
        Write-Information $String
        $y = $Host.UI.RawUI.CursorPosition.Y - 1
        $pos = New-Object System.Management.Automation.Host.Coordinates($x,$y)
        if ($InformationPreference -ne "SilentlyContinue") {
            $Host.UI.RawUI.CursorPosition = $pos
        }
    } else {
        Write-Information $String
    }
    if ($ForegroundColor) {
        $Host.UI.RawUI.ForegroundColor = $orgForegroundColor
    }
    if ($BackgroundColor) {
        $Host.UI.RawUI.BackgroundColor = $orgBackgroundColor
    }
}
```
　
クリエイティブ・コモンズ 表示 - 継承 4.0 国際