PowerShell

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

概要

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

背景

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

※Version5.0より前の話

Write-Host の置き換え

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

# by earthdiver1

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

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

  • -NoNewline オプションの動作も模倣します。ただし、全角文字が混在する文字列がウィンドウの右端で折り返しになる場合に水平位置が少しずれることがあります。
  • 別途投稿している Get-StringWidth 関数を使用しています。
# by earthdiver1
$InformationPreference = "Continue"

Function Write-Host() {
    param( 
        [Parameter(Position=0, ValueFromPipeline=$True, ValueFromRemainingArguments=$True)][String] $String, 
        [Switch] $NoNewLine, 
        [ConsoleColor] $ForegroundColor,
        [ConsoleColor] $BackgroundColor
    )
    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 国際