Edited at

バッチファイルから PowerShell を呼び出す方法

More than 3 years have passed since last update.


背景

PowerShell でスクリプトを書くメリットの一つとしては、Windows7 以降ならデフォルトで入っているので、Windows を使っている人になら書いたスクリプトを渡して、そのまま使えることにあると思っている。(しかも .NET が使える)

ただデフォルトの設定だと PowerShell のスクリプトを実行できなかったり、ダブルクリックで実行できないのも、ちょっと不便だなと感じるので、その回避方法をメモする。


普通に引数で渡す

一番単純な方法は、PowerShell のスクリプトを呼び出すバッチファイルを、いっしょに置いておくことだと思う。

デフォルトだと ExecutionPolicy の値でスクリプトの実行ができないのでオプションで指定する。


hello.bat

powershell -NoProfile -ExecutionPolicy Unrestricted .\hello.ps1



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