Open7

Powershellスクリプトのショートカットを作るときの引数の扱われ方について

aikigeaikige

PowerShellスクリプトにドラッグアンドドロップ(D&D)でファイルを渡すためにショートカットを作る際に、一般的には「リンク先」を以下のようにしたショートカットを作ることが推奨されているようです。

powershell.exe -File "C:/スクリプトの/フルパス/表記.ps1"

このショートカットに、D&Dでファイルをドロップすると、$Args という配列にドロップしたファイル(複数可)が配列として格納されます。

aikigeaikige

ところが、以下のように書いてもスクリプト自体は起動することが可能です。

powershell.exe "C:/スクリプトの/フルパス/表記.ps1"

しかし、こちらのやり方で起動した場合、D&Dで渡したファイルのパスに空白文字が含まれていると、空白文字でばらばらになった引数が配列 $Args に入ります。

例えば、C:\Some Directory\Scriptname Sample.txt という名前のファイルをD&Dで渡すと、$Args は以下のような感じになります。

PS> echo $Args
C:\Some
Directory\Scriptname
Sample.txt
PS> echo $Args.Length
3

一つしか渡していないファイル名が、スペースで分割された形になります。

このあたりの挙動の差が、ドキュメントを読んでもよくわからない。

aikigeaikige

この現象を観測しているのが Windows 10 なのですが、この場合 powershell.exe で呼び出されるのは PowerShell 5.1 系になります。

おそらく、以下の記事の記載がこの現象の解釈に役立ちそうです

https://learn.microsoft.com/ja-jp/powershell/scripting/whats-new/differences-from-windows-powershell?view=powershell-7.4

最初の位置指定パラメーターが -Command から -File に変更されました。 この変更により、Windows 以外のプラットフォームの PowerShell 以外のシェルから実行されている PowerShell スクリプトの #! (シバンともいいます) の使用方法が修正されます。 これは、-File を指定せずに pwsh foo.ps1 または pwsh fooScript のようなコマンドを実行できることも意味します。 ただし、この変更によって、pwsh.exe -Command Get-Command などのコマンドを実行しようとする場合は -c または -Command を明示的に指定する必要があります。

つまり、PowerShell 5.1 系の場合、最初の位置指定パラメータは -Command で、PowerShell 7.x 系では最初の位置指定パラメータは -File になっているようです。

位置指定パラメータとはなにか?については、以下のヘルプに書かれています:

https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_parameters?view=powershell-5.1

が、PowerShell.exe 自身の仕様

https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_powershell_exe?view=powershell-5.1

には、どの引数が位置引数になっているかは明確に書いてありません…

aikigeaikige

まあ、とりあえず -Command が第一の位置引数だと仮定して考えてみましょう。
前掲の「PowerShell.exe について」の以下の文言が重要そうです。

Command の値が文字列の場合は、その後のすべての引数が実行するコマンドの一部として解釈されるため、コマンドは pwsh の最後のパラメーターである必要があります。

つまり、ショートカットとして2つ目の例のように明示的なオプション名をつけずに powershell.exe を呼び出している場合、すべての引数は単一のコマンド文字列として扱われ、PowerShell のコマンドインタプリタに解釈されている可能性が高いです。今回の場合、そのコマンド文字列は以下になっているのではないかと推定されます。

"C:/スクリプトの/フルパス/表記.ps1" C:\Some Directory\Scriptname Sample.txt

こうすると、たしかにスペースでばらばらになりそうですね。

aikigeaikige

とりあえず、powershell.exe を使ったショートカットを作るのであれば、最初に示したように -File オプションでスクリプトを指定してあげたほうが謎挙動を防げるので良いでしょう。

aikigeaikige

-File の場合の仕様は

-File <FilePath> <Args>

という想定なので、区切りをきちんと解釈してくれるのだろうな…とは思うのですが、このあたりの差異が文書で明確に示されていないように感じてもやもやします。
もう少し明確なロジックが見つかるかもしれないので、このスクラップはオープンにしておきます。