しばたテックブログ

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

powershell.exe -Commandパラメーターの謎挙動について (解決編)

stknohg.hatenablog.jp

前のエントリの謎挙動についてですが、やっと何が起きてるかわかったので追記エントリを書きます。

解決のヒント

解決のヒントは以下の記事にありました。

Escaping in PowerShell

この記事ではPowerShellからADSIの機能を呼び出した場合について触れています。
内容を要約すると、PowerShellで文字列のエスケープが行われた後にADSI側でもエスケープされるので文字列に対してADSIのエスケープキャラクタも設定する必要があるよ、ということを言っています。

-Commandオプションの挙動について

このヒントをベースに改めてpowershell.exe -Commandオプションの挙動を調べてみたところ、-Commandパラメーターに渡された文字列は以下の流れで解釈されることがわかりました。


  1. powershell.exe -Commandを呼び出す。

  2. -Commandに指定された文字列に対しPowerShellの文字列展開および特殊文字のエスケープが行われる。

  3. エスケープ後powershell.exeが起動する。

  4. powershell.exeに引き渡された文字列は(恐らく)コンソールホストの仕様に基づいてさらに特殊文字のエスケープが行われる。

  5. エスケープ後の文字列が新しいプロセスのPowerShellエンジンによって解釈され処理の実行に移る。


4.の処理については予測が入っているのですが、挙動としてはコマンドプロンプト(cmd.exe)のエスケープ処理と同じになっています。
この点からコマンドプロントとPowerShellに共通するコンソールホスト(conhost.exe)の仕様に基づくものと予測しています。

挙動を確認してみる

前エントリの例を元に再度挙動を確認してみます。
元なるコードは変更せず以下となります。

$word='PowerShell'; Write-Output "Windows $word"

謎挙動を示すコードは以下でした。

# PowerShellコンソールからさらにPowerShellを起動
powershell.exe -Command "`$word='PowerShell'; Write-Output `"Windows `$word`""

このコードが実行されると先ずPowerShellの文字列展開およびエスケープがなされ、powershell.exeに引き渡される-Commandの内容は以下になります。

"$word='PowerShell'; Write-Output "Windows $word""

これがコンソールホストの仕様に基づいてさらに処理されます。

その内容を直接確認することは出来ないので、コマンドプロンプトからpowershell.exeを起動することで間接的にどの様な挙動をするか確認してみます。

REM コマンドプロンプトから起動
powershell.exe -Command "$word='PowerShell'; Write-Output "Windows $word""

結果は以下。

f:id:stknohg:20160330194019p:plain

見事に文字列が分割されてしまいました。

そして、コンソールホストの仕様では空白付きの文字列を\でエスケープすることができます。
"Windows $word"の部分に対して\"Windows $word"とすることで空白付き文字列を正しく認識させることがます。
この仕様については正直よくわかりません...

コマンドプロンプトから以下のコードを実行してこの動作を確認してみます。

REM コマンドプロンプトから起動
powershell.exe -Command "$word='PowerShell'; Write-Output \"Windows $word""

結果下図の様になり期待した動作をします。

f:id:stknohg:20160330194056p:plain

ですので、最終的にPowerShellで呼び出すコードは、コンソールホストによるエスケープを考慮して以下になります。

# PowerShellコンソールからさらにPowerShellを起動
powershell.exe -Command "`$word='PowerShell'; Write-Output \`"Windows `$word`""

実行するときちんと期待した動作をしてくれます。

f:id:stknohg:20160330194231p:plain

謎挙動が解決できたので嬉しい限りです。

補足

コマンドプロンプト(コンソールホスト)の文字列の展開やエスケープについてはこちらのサイトが詳しいです。

thinca.hatenablog.com

一読した印象ですが謎だらけですね...
あまり考えたくない感じです。