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! を表現する方法でよく出される方法です。

"Hello World!"

コンソール画面に出力結果が表示されました。

Hello World!

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

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

"で変数を括った場合、それは展開されて出力されます。

$hoge = "Hello World!"
"$hoge"

$hoge が展開されて 中に含まれた Hello World! が出力されました。

Hello World!
3. ' で括る

"(ダブルクォート)で括る以外に'(シングルクォート)で括っても[System.String]へ動的に型付けられされます。

'Hello World!'

コンソール画面に出力結果が表示されました。

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

'で変数を括った場合、それは展開されずに出力されます。

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

$fuga が展開されず、 そのまま $fuga として出力されました。

$fuga

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

f:id:guitarrapc_tech:20140117045125p:plain

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

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

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

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

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 でこのように表現されます。

Write-Output -InputObject "Hello World!"

f:id:guitarrapc_tech:20140117055250p:plain

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

Write-Output "Hello World!"
Hello World!

この Write-Output Cmdlet は PowerShell の文字列出力のもっとも基礎であり、重要です。

なぜなら、これから紹介する他の Write-Hostを初めとする Cmdlet がホスト画面への出力のみに対して、Write-Output は出力 がパイプライン や ホスト画面 にわたります。

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

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

[System.String] としてパイプラインを渡せる = 利用者の任意で出力を操作できる 文字列出力 Cmdletは Write-Outputだけ なため、多くの場面では Write-Host ではなく Write-Output を利用すべきと最近はそこかしこで言われています。*2

System.String や System.Int が出力される内容を実行すると暗黙的に利用されるため、Write-Output を明示することは少なく、またそれでいいと思います。実際"hoge"'huga' あるいは パイプラインの最後で | $_ とすることは多いです。

しかし、Write-Host などと異なり、Write-Output や " 、' などの出力は、さらに後ろに |(パイプライン) をつなげるだけで後続に処理を渡せることは覚えておいてください。

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

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

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

Write-Host -Object "Hello World!"

f:id:guitarrapc_tech:20140117055147p:plain

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

Write-Host "Hello World!"
Hello World!

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

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

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

Write-Host "Hello World!" -ForegroundColor Magenta

f:id:guitarrapc_tech:20140117054306p:plain

繰り返しますが、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の色で出力されます。

f:id:guitarrapc_tech:20140117054951p:plain

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 を付けて強制的に出力します)

f:id:guitarrapc_tech:20140117055740p:plain

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

f:id:guitarrapc_tech:20140117060557p:plain

-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 を付けて強制的に出力します)

f:id:guitarrapc_tech:20140117061403p:plain

$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

f:id:guitarrapc_tech:20140117061848p:plain

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

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

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

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

Write-Error -Message "Hello World!"

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

f:id:guitarrapc_tech:20140117063907p:plain

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に戻す
}

f:id:guitarrapc_tech:20140117063724p:plain

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

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

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

11. $Host.UI.WriteLine()

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

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

f:id:guitarrapc_tech:20140117064806p:plain

12. $Host.UI.WriteWarningLine()

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

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

f:id:guitarrapc_tech:20140117064827p:plain

13. $Host.UI.WriteVerboseLine()

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

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

f:id:guitarrapc_tech:20140117064848p:plain

14. $Host.UI.WriteDebugLine()

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

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

f:id:guitarrapc_tech:20140117064910p:plain

15. $Host.UI.WriteErrorLine()

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

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

f:id:guitarrapc_tech:20140117065200p:plain

.NET Framework による単純な出力

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

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

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

[System.Console]::WriteLine("Hello World!")
Hello World!
17. [System.Console]::Write()

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

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

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

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

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

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

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

$hoge = "Hello World!"
@"
全部ストリング
だよー
変数展開するのー
$hoge
"@
全部ストリング
だよー
変数展開するのー
Hello World!
19. @' によるヒアストリング

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

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

まとめ

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

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

  • Object出力できる Write-Output は影の功労者です
  • Write-Host は悪!です。Write-Output や Write-Verbose などに置きかえれないか再考できるといいですね
  • [Console]::WriteLine() は高速です。早いは正義!使うかは別!
  • $Host.UI は、開発中でない限り使わないです
  • ヒアストリングはそれはそれで便利です。あまり使わないですが
  • 私が一番好きなのは、 Write-Debug で次が Write-Verbose です。はい

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

*1:変数はオレンジ

*2:もちろん状況にもよります