tech.guitarrapc.cóm

Technical updates

PowerShell での文字列出力について考える

なんだかPowerShellタグの記事200件目らしいです。ブログ開始して一年余りですが、少しはPowerShellを知りたい人に紹介できているのでしょうか。

今回は基本に立ち戻って文字列[System.String]を出力するときの方法について考えてみます。一通り押さえたら想定以上に長くなったので、時間のあるときにでもどうぞ。

説明一覧

# 分類 処理
1 "'による単純な出力 "
2 "での変数展開
3 '
4 'での変数展開
5 Write-* Cmdlet による単純な出力 Write-Output
6 Write-Host
7 Write-Warning
8 Write-Verbose
9 Write-Debug
10 Write-Error
11 $Host.UI による単純な出力 $Host.UI.WriteLine()
12 $Host.UI.WriteWarningLine()
13 $Host.UI.WriteVerboseLine()
14 $Host.UI.WriteDebugLine()
15 $Host.UI.WriteErrorLine()
16 .NET Framework による単純な出力 [System.Console]::WriteLine()
17 [System.Console]::Write()
18 ヒアストリングによる単純な出力 @" によるヒアストリング
19 @' によるヒアストリング

"'による単純な出力

1. " で括る

最も基礎になりますが、PowerShellでHello World! を表現する方法でよく出される方法です。コンソール画面に出力結果が表示されます。

PS> "Hello World!"
Hello World!

PowerShellでは、文字列を"(ダブルクォート) で括ると[System.String]へ動的に型付けられされます。

2. "で括ると変数は展開される

"で変数を括った場合、それは展開されて出力されます。$hogeが展開されて中に含まれたHello World! が出力されました。

PS> $hoge = "Hello World!"
PS> "$hoge"
Hello World!

3. ' で括る

"(ダブルクォート)で括る以外に'(シングルクォート)で括っても[System.String]へ動的に型付けられされます。コンソール画面に出力結果が表示されました。

PS> 'Hello World!'
Hello World!

4. 'で括ると変数は展開されない

'で変数を括った場合、それは展開されずに出力されます。$fugaが展開されずそのまま$fugaとして出力されました。

PS> $fuga = "Hello World!"
PS> '$fuga'
$fuga

'で括った変数は、PowerShell ISEでシンタックスハイライトされると文字列の茶色なのでわかりやすいでしょう。*1

image

こういう機械的に解釈すべきことは、シンタックスハイライトのあるエディタに任せるべきですね。もちろん他にも理由はありますが。

Write-* Cmdlet による単純な出力

続いてCmdletによる文字列出力を見てみましょう。

Microsoft.PowerShell.Utilityモジュールに含まれる、Write-* となる一連のCmdletですが、Write-Progreeは、String表現ではなく進捗表現用のCmdletのため、ここでは説明を省きます。

PS> Get-Command -Module Microsoft.PowerShell.Utility -Verb Write
CommandType Name           ModuleName
----------- ----           ----------
Cmdlet      Write-Debug    Microsoft.PowerShell.Utility
Cmdlet      Write-Error    Microsoft.PowerShell.Utility
Cmdlet      Write-Host     Microsoft.PowerShell.Utility
Cmdlet      Write-Output   Microsoft.PowerShell.Utility
Cmdlet      Write-Progress Microsoft.PowerShell.Utility # これは省く
Cmdlet      Write-Verbose  Microsoft.PowerShell.Utility
Cmdlet      Write-Warning  Microsoft.PowerShell.Utility

5. Write-Output Cmdletで出力する

さきほどの"'だけで出力された文字列は、裏でWrite-Outputとして処理されています。

つまり先ほどの出力は、Write-Output Cmdletでこのように表現されます。

PS> Write-Output -InputObject "Hello World!"

image

-InputObjectはpipelineからの入力を受け取るので、パラーメータ指定を省略すると入力文字列を -InputObjectパラメータに自動的に紐づけて出力します。

PS> Write-Output "Hello World!"
Hello World!

Write-OutputCmdletはPowerShellの文字列出力のもっとも基礎であり重要です。 なぜなら、これから紹介する他のWrite-Hostを初めとするCmdletがホスト画面への出力のみに対して、Write-Outputは出力がパイプラインやホスト画面にわたります。

# ホスト画面に Hello World!hogehoge. と出力される。
PS> Write-Output "Hello World!" | % {$_ + "hogehoge."}
Hello World!hogehoge.

# ホスト画面には出力せず、現在のディレクトリ直下 helloworld.txt に Hello World! へ出力する。
PS> Write-Output "Hello World!" | Out-File .\helloWorld.txt

[System.String]としてパイプラインを渡せて、利用者の任意で出力を操作できる文字列出力CmdletはWrite-Outputだけ、と考えてもいいでしょう。多くの場面においてWrite-HostではなくWrite-Outputを利用すべきと最近はそこかしこで言われています。System.StringSystem.Intが出力される内容を実行すると暗黙的に利用されるためWrite-Outputを明示することは少ないです。実際"hoge"'huga'あるいはパイプラインの最後で| $_とすることは多いです。しかしWrite-Hostと異なりWrite-Output"'などの出力は、さらに後ろに|(パイプライン) をつなげるだけで後続に処理を渡せることは覚えておいてください。

6. Write-Host Cmdletでホスト画面へのみ出力する

Write-Hostはホスト画面出力専用のCmdletです。Write-Outputのようにオブジェクト出力されることがないため、|パイプラインを渡すことはできません。出力する内容は、 -Objectパラメータに受けます。

Write-Host -Object "Hello World!"

image

-Objectはpipelineからの入力を受け取るので、パラーメータ指定を省略すると入力文字列を -Objectパラメータに自動的に紐づけて出力します。

PS> Write-Host "Hello World!"
Hello World!

-NoNewlineスイッチを利用すると改行せずに実行を終えます。.NETでいう、System.Console.Write()と一緒ですね。

PS> Write-Host "Hello World!" -NoNewline
PS> Write-Host "Hello World!" -NoNewline
Hello World!Hello World!

ホスト画面出力専用だけあり、-ForegroundColorで文字色、-BackgroundColorでは背景色を任意に指定できます。

Write-Host "Hello World!" -ForegroundColor Magenta

image

繰り返しますが、Write-Hostに入力されたオブジェクトは必ずホスト画面に出力され利用者は出力先をパイプラインや$nullに渡すといった制御ができないため、使うときは慎重に検討してください。私はどうしても出力したい場面でしか使いません。

PowerShell WordFlowで利用できないことも注意です。

7. Write-Warning Cmdlet で警告をホスト画面へのみ出力する

Write-Warningは、Write-Hostと同様に、ホスト画面出力専用のCmdletです。Write-Outputのようにオブジェクト出力されることがないため、|パイプラインを渡すことはできません。

出力する内容は、 -Messageパラメータに受けます。

Write-Warning -Message "Hello World!"

出力には自動的に警告: やWARNING: と頭に表示され、$host.PrivateData.WarningForegroundColorの色で出力されます。

image

Warning出力されるかは、システム変数の$WarningPreferenceで制御できます。

SilentlyContinueの時は、 -WarningActionで状態を指定しないと出力されません。が、デフォルトのContinueなどの場合は、必ずWrite-Warningの内容がホスト出力されます。

デフォルトではWrite-Warningに入力されたオブジェクトは、必ずホスト画面に出力され利用者は出力先をパイプラインや$nullに渡すといった制御ができないため、使うときは慎重に検討してください。

8. Write-Verbose Cmdlet でVerbose時にのみホスト画面へ出力する

Write-Verboseは、Write-Hostと同様に、ホスト画面出力専用のCmdletです。また、Write-Outputのようにオブジェクト出力されることがないため、|パイプラインを渡すことはできません。

出力する内容は、 -Messageパラメータに受けます。PowerShell起動直後に以下を実行しても出力されません。

Write-Verbose -Message "Hello World!"

Verbose出力されるかは、システム変数の$VerbosePreferenceで制御できます。

デフォルトのSilentlyContinue時は-Verboseを付けないと出力されません。が、それ以外のContinueなどの場合は、必ずWrite-Verboseの内容がホスト出力されます。

出力には自動的に詳細: やVERBOSE: と頭に表示され、$host.PrivateData.VerboseForegroundColorの色で出力されます。(今回は -Verboseを付けて強制的に出力します)

image

$orgVerbosePreference = $VerbosePreference   # 現在のPreferenceを格納
$VerbosePreference = "Continue"              # Continue に変更して、必ず Verbose出力されるようにする
Write-Verbose -Message "Hello World! 1"      # 出力される
$VerbosePreference = $orgVerbosePreference   # 元の SilentryContinueに戻す
Write-Verbose -Message "Hello World! 2"      # 出力されない

image

-Verboseスイッチで出力タイミングを制御できるため、より詳細にFunctionの動きを利用者のために出力するときなどに活用されます。

9. Write-Debug Cmdlet でデバッグ停止しつつ停止メッセージをホスト画面へのみ出力する

Write-Debugは、Write-Hostと同様に、ホスト画面出力専用のCmdletです。Write-Outputのようにオブジェクト出力されることがないため、|パイプラインを渡すことはできません。

出力する内容は、 -Messageパラメータに受けます。PowerShell起動直後に以下を実行しても出力されません。

Write-Debug -Message "Hello World!"

Debug実行されるかは、システム変数の$DebugPreferenceで制御できます。

デフォルトのSilentryContinueの時は、 -Debugを付けないと出力されません。が、それ以外のContinueなどの場合は、必ずWrite-Debugの内容がホスト出力されます。

出力には自動的にデバッグ: やDEBUG: と頭に表示され、$host.PrivateData.DebugForegroundColorの色で出力されます。(今回は -Debugを付けて強制的に出力します)

image

$orgDebugPreference = $DebugPreference     # 現在のPreferenceを格納
$DebugPreference = "Continue"              # Continue に変更して、必ず Verbose出力されるようにする
Write-Debug -Message "Hello World! 1"      # Debug実行される
$DebugPreference = $orgDebugPreference     # 元の SilentryContinueに戻す
Write-Debug -Message "Hello World! 2"      # Debug実行されない

また、Cmdletやファンクションにおいてコモンパラメータの-Debugをつけてデバッグ実行すると、Write-Debugの箇所でデバッグ停止します。

function hoge
{
    [CmdletBinding()]
    param()

    "Debug まえ"
    Write-Debug -Message "Hello World!"
    "Debug あと"
}
hoge -Debug

image

-Debugスイッチで出力タイミングを制御できるため、デバッグしつつ動作を追うときには欠かせません。

10. Write-Error Cmdlet でエラーメッセージをホスト画面へのみ出力する

Write-Errorは、Write-Hostと同様に、ホスト画面出力専用のCmdletです。-Errorがついた処理でしか出力されません。また、Write-Outputのようにオブジェクト出力されることがないため、|パイプラインを渡すことはできません。

出力する内容は、 -Messageパラメータに受けます。

Write-Error -Message "Hello World!"

出力はエラー表示され、$host.PrivateData.ErrorForegroundColorの色で出力されます。(今回は -Errorを付けて強制的に出力します)

image

Error出力時の動作は、システム変数の$ErrorActionPreferenceで制御できます。

デフォルトのSilentryContinueの時は、 Write-Errorをしても動作が継続しtyr{}catch{}でも捕捉できません。が、Stopの場合は、必ずWrite-Errorやエラー時点でcatch節に補足されます。

try
{
    $orgErrorActionPreference = $ErrorActionPreference  # 現在のPreferenceを格納
    $ErrorActionPreference = "Stop"                     # Stop に変更して、必ず Catch に捕捉されるようにする
    Write-Error -Message "Hello World! 1"               # Catchに補足実行される
    Write-Error -Message "Hello World! 2"               # 実行されない
}
catch
{
    "捕捉"
}
finally
{
    $ErrorActionPreference = $orgErrorActionPreference  # 元の SilentryContinueに戻す
}

image

PowerShellでのエラー捕捉に、とても重要です。

$Host.UI による単純な出力

$Host.UIでの出力という手もあります。これらは、Write-* それぞれのテストとして利用しやすいですが各Preferenceで動作が左右されず必ず実行されるため気を付けてください。

11. $Host.UI.WriteLine()

単純なホスト出力です。Write-Outputのようにオブジェクト出力されることがないため、|パイプラインを渡すことはできません。 Write-Hostとよく似ていますね。

$Host.UI.WriteLine("Hello World!")

image

12. $Host.UI.WriteWarningLine()

$host.PrivateData.WarningForegroundColorの色でホスト出力されます。Write-Outputのようにオブジェクト出力されることがないため、|パイプラインを渡すことはできません。 Write-Warningとよく似てますが、動作はWrite-Hostです。

$Host.UI.WriteWarningLine("Hello World!")

image

13. $Host.UI.WriteVerboseLine()

$host.PrivateData.VerboseForegroundColorの色でホスト出力されます。Write-Outputのようにオブジェクト出力されることがないため、|パイプラインを渡すことはできません。 Write-Verboseとよく似てますが、動作はWrite-Hostです。

$Host.UI.WriteVerboseLine("Hello World!")

image

14. $Host.UI.WriteDebugLine()

$host.PrivateData.DebugForegroundColorの色でホスト出力されます。Write-Outputのようにオブジェクト出力されることがないため、|パイプラインを渡すことはできません。 Write-Debugとよく似てますが、動作はWrite-Hostです。

$Host.UI.WriteDebugLine("Hello World!")

image

15. $Host.UI.WriteErrorLine()

$host.PrivateData.ErrorForegroundColorの色でホスト出力されます。Write-Outputのようにオブジェクト出力されることがないため、|パイプラインを渡すことはできません。 Write-Errorとよく似てますが、動作はWrite-Hostです。

$Host.UI.WriteErrorLine("Hello World!")

image

.NET Framework による単純な出力

もはやそのままですね。ホスト出力です。他のどれよりも高速にホスト出力されるのは特筆です。

16. [System.Console]::WriteLine()

末尾を改行してホスト出力されます。Write-Outputのようにオブジェクト出力されることがないため、|パイプラインを渡すことはできません。

PS> [System.Console]::WriteLine("Hello World!")
Hello World!

17. [System.Console]::Write()

末尾を改行せずホスト出力されます。Write-Outputのようにオブジェクト出力されることがないため、|パイプラインを渡すことはできません。

PS> [System.Console]::Write("Hello World!")
Hello World!

ヒアストリングによる単純な出力

ヒアストリング (Here-Strings)は、開始識別子の@" か @' で始まって、終了識別子の"@ か '@ で終わるものです。

間に入った入力値はすべて文字列として処理されます。終了識別子の前にスペースなどを含めて何も入らないようにする必要があるため、注意してください。

18. @" によるヒアストリング

"で括った場合と同じように変数展開します。

PS> $hoge = "Hello World!"
PS> @"
全部ストリング
だよー
変数展開するのー
$hoge
"@
全部ストリング
だよー
変数展開するのー
Hello World!

19. @' によるヒアストリング

'で括った場合と同じように変数展開しません。

PS> $fuga = "Hello World!"
PS> @'
全部ストリング
だよー
変数展開しないのー
$fuga
'@
全部ストリング
だよー
変数展開しないのー
$fuga

まとめ

コードを置いておきます。

長い記事になりましたが、参考になれば幸いです。

  • Object出力できるWrite-Outputは影の功労者
  • Write-Hostは避けて、Write-OutputWrite-Verboseなどに置きかえられないか検討
  • [Console]::WriteLine()は高速
  • $Host.UIは、開発中でない限り使わない
  • ヒアストリングは便利

私が一番好きなのはWrite-DebugWrite-Verboseです。

次回は、文字列の表現についてです。

*1:変数はオレンジ