前のエントリの謎挙動についてですが、やっと何が起きてるかわかったので追記エントリを書きます。
解決のヒント
解決のヒントは以下の記事にありました。
この記事ではPowerShellからADSIの機能を呼び出した場合について触れています。
内容を要約すると、PowerShellで文字列のエスケープが行われた後にADSI側でもエスケープされるので文字列に対してADSIのエスケープキャラクタも設定する必要があるよ、ということを言っています。
-Commandオプションの挙動について
このヒントをベースに改めてpowershell.exe -Command
オプションの挙動を調べてみたところ、-Command
パラメーターに渡された文字列は以下の流れで解釈されることがわかりました。
powershell.exe -Command
を呼び出す。-Command
に指定された文字列に対しPowerShellの文字列展開および特殊文字のエスケープが行われる。エスケープ後
powershell.exe
が起動する。powershell.exe
に引き渡された文字列は(恐らく)コンソールホストの仕様に基づいてさらに特殊文字のエスケープが行われる。エスケープ後の文字列が新しいプロセスの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""
結果は以下。
見事に文字列が分割されてしまいました。
そして、コンソールホストの仕様では空白付きの文字列を\
でエスケープすることができます。
"Windows $word"
の部分に対して\"Windows $word"
とすることで空白付き文字列を正しく認識させることがます。
この仕様については正直よくわかりません...
コマンドプロンプトから以下のコードを実行してこの動作を確認してみます。
REM コマンドプロンプトから起動 powershell.exe -Command "$word='PowerShell'; Write-Output \"Windows $word""
結果下図の様になり期待した動作をします。
ですので、最終的にPowerShellで呼び出すコードは、コンソールホストによるエスケープを考慮して以下になります。
# PowerShellコンソールからさらにPowerShellを起動 powershell.exe -Command "`$word='PowerShell'; Write-Output \`"Windows `$word`""
実行するときちんと期待した動作をしてくれます。
謎挙動が解決できたので嬉しい限りです。
補足
コマンドプロンプト(コンソールホスト)の文字列の展開やエスケープについてはこちらのサイトが詳しいです。
一読した印象ですが謎だらけですね...
あまり考えたくない感じです。