Chapter 15

(極道)バッチファイルだと思った? ざんねん!PowerShell でした!

hymkor
hymkor
2020.10.21に更新
@set args=%*
@powershell "iex((@('')*3+(cat '%~f0'|select -skip 3))-join[char]10)"
@exit /b %ERRORLEVEL%

という3行をバッチファイルの先頭に書いておくと、4行目から PowerShell で書けるよ!
(これ、実際に nyagos のビルドスクリプトの make.cmd で使っています)

何をやっているかというと、powershell でバッチファイル自身を読み込んで、1~3行目を空行に差し替えてから全行を eval してるんですわ。要は PowerShell向けの Shebang みたいなもんです

こんなことしなくても、普通に PowerShell のスクリプトをそのまま配布すればいいじゃないというのがあるんですが:

  • 通常 PowerShell のスクリプトをどこでも確実に実行するにはセキュリティーエラーを抑制するために powershell -ExecutionPolicy RemoteSigned -File スクリプトファイル名 とすごく長く書かなくてはいけないが、このようにバッチファイルに組み込むと「バッチファイル名」だけでよい(無論、一度 Set-ExcutionPolicy をすればいちいち書かなくてもよいが、結局、それ配布する人全員相手にいちいち説明するの?という問題がある)
  • powershell -ExecutionPolicy RemoteSigned -File スクリプトファイル名 をスクリプトファイルとは別のバッチファイルに書けば、ユーザはそのバッチファイルを実行すればよいだけだが、配布ファイルが複数になってしまう。このようにバッチファイルに組み込んでしまうと、ワンファイルだけでよい

なお、バッチファイルに与えられたパラメータは環境変数 args に放り込まれているので PowerShell 側では $env:args を分割して

$args = @( ([regex]'"([^"]*)"').Replace($env:args,{
        $args[0].Groups[1] -replace " ",[char]1
    }) -split " " | ForEach-Object{ $_ -replace [char]1," " })

とすれば %1%9$args[0]$args[8] で扱えるようになります。


ただね、ここまで無理やりなことまでやらないと運用しづらい PowerShell って何だろう…って思いません?(正直、Go言語のバイナリさくっと書いた方が早くね…と)