背景
PowerShell でスクリプトを書くメリットの一つとしては、Windows7 以降ならデフォルトで入っているので、Windows を使っている人になら書いたスクリプトを渡して、そのまま使えることにあると思っている。(しかも .NET が使える)
ただデフォルトの設定だと PowerShell のスクリプトを実行できなかったり、ダブルクリックで実行できないのも、ちょっと不便だなと感じるので、その回避方法をメモする。
普通に引数で渡す
一番単純な方法は、PowerShell のスクリプトを呼び出すバッチファイルを、いっしょに置いておくことだと思う。
デフォルトだと ExecutionPolicy の値でスクリプトの実行ができないのでオプションで指定する。
powershell -NoProfile -ExecutionPolicy Unrestricted .\hello.ps1
Write-Host "Hello, World!!"
ただちょっとした処理を書くために使いたいケースが多いのに、ファイルが 2 つに分かれてしまうのは、ちょっと嫌だなと感じる。
一行だけなら、こうも書ける
powershell -NoProfile -ExecutionPolicy Unrestricted -Command "& { echo 'Hello, World!!' }"
ただこれで書くと、少し量が増えただけでゴチャゴチャする。
1 つのファイルにまとめる
これが今回メモしておこうと思った内容。
最近、Scala を使いだしたら、Scala だとバッチファイルに、
::#!
@echo off
call scala %0 %*
goto :eof
::!#
println("Hello, World!!")
とか書くと、Scala がインストールされている環境なら動いてしまう。
これがなぜ動くを、よく見てみると PowerShell でもできるのではないかと思った。
ググってみると、 stackoverflow に説明してあるのがあった。
さらにググってみると、 Make it easier to wrap powershell scripts in batch files というのを見つけた。
@powershell -NoProfile -ExecutionPolicy Unrestricted "$s=[scriptblock]::create((gc \"%~f0\"|?{$_.readcount -gt 1})-join\"`n\");&$s" %*&goto:eof
Write-Host "Hello, World!!"
とりあえず、これで動くらしい。というか動く。
やってることは、両方ともバッチファイルの中で自分自身を呼び出して、その呼び出す記述を呼び出す側で無視するという感じ。
これなら多少コードが増えても、そこそこ使える気がする。
Base64 encode
ついでに、Base64 encode するというのもあった。
ちょっと面倒な気もするけど、これはこれで何かに使えるかもしれない。
powershell -NoProfile -ExecutionPolicy Unrestricted -EncodedCommand ZQBjAGgAbwAgACIAaABlAGwAbABvACIAOwAgAGUAYwBoAG8AIAAiAHcAbwByAGwAZAAiADsA
エンコードする PowerShell ワンライナー
# 文字コードは適宜調整すること
# 元にする PowerShell のコードは行末に ; を付けること
"powershell -NoProfile -ExecutionPolicy Unrestricted -EncodedCommand $([Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($(cat .\hello.ps1))))" | Out-File .\base64encoded.bat -Encoding Default