1. Qiita
  2. 投稿
  3. batch

Windowsでshebangもどき、またはバッチにスクリプトを埋め込む方法

  • 57
    いいね
  • 9
    コメント
この記事は最終更新日から1年以上が経過しています。

rubyやperl製のスクリプトは、パスを通せばコマンドプロンプトで通常のexeツールのように使うことができます。ただし、 hoge.rb のように拡張子を含めて打ちこむ必要があります。これを省略するにはPATHEXTをいじるのが簡単です。

しかし、スクリプトファイルをすでにインタプリタでなくエディタなどに関連付けしている場合はこの方法は使えません。

そこで、スクリプトを標準でPATHEXTに含まれているバッチファイルにラッピングします。つまり、.rb.pl.pyなどの拡張子を.batに変えて無理やり使うということです。味噌はバッチ引数の展開オプションです (詳細は help call を参照)。

以下の例はすべてインタプリタにパスが通っていることを前提としています。

バッチにスクリプトを埋め込む

ほとんどの言語に用意されているshebang向けオプションを使います。

Rubyの場合

Rubyの-xオプション#!の行に当たるまでを無視するので、一行目をバッチとして書くことができます。
そのまま実行が続くとスクリプトの内容までバッチファイルとして実行されるので、exitでそれを阻止します。

@ruby -x "%~f0" %* & exit /b
#!ruby
# 以下スクリプトの内容

Perlの場合

Rubyと同様です。

@perl -x "%~f0" %* & exit /b
#!perl
# 以下スクリプトの内容

余談ですが、PerlとRubyは頭だけでなく__END__を使ってスクリプトの終わりを明示できるので、exitの代わりにgoto を使うことで複数のRubyスクリプトを内蔵することができます(使い道はともかく)。

Pythonの場合

Pythonの-x#!を探さず一行目を飛ばすだけなので、shebangを書く必要はありません(書いてもかまいません)。

@python -x "%~f0" %* & exit /b
# 以下スクリプトの内容

GHCの場合

runghcはPOSIXの場合同様 Literate Haskell を使うことでバッチ部分を迂回できます。

@runghc --ghc-arg=-x --ghc-arg=lhs "%~f0" %* & exit /b
\begin{code}
-- スクリプトの内容
\end{code}

GHCiで読む際はghci -x lhs hoge.batのようにすると読み込めます。

Gaucheの場合

Gaucheは#!のみを見るので、バッチファイルの @ を無視してくれません。

追記: しかし、; と組み合わせることで迂回することが出来ます。 これはGaucheの文法では行コメントとして解釈される一方、コマンドプロンプトの文法では(おそらく)空白として解釈されて無害になります。anohanaさんよりコメントで教えていただきました。ありがとうございました。

;@gosh -x "%~f0" %* & exit /b
; 以下スクリプトの内容

Luaの場合

hymkorさんの記事で、ラベル構文とコメント文を組み合わせて埋め込みに成功されています。

::rem:: --[[
@lua "%~f0" %1 %2 %3 %4 %5 %6 %7 %8 %9 & exit /b
]]--

print(string.format("[%s]",arg[1]))
-- vim:set ft=lua:

標準入力から実行する方法

Perlの例

Perlなど上で挙がった多くのスクリプト処理系は、ファイル名を - とすることで標準入力からスクリプトを読んで実行することができます。-x に比べると $0 が設定されないのが玉に瑕ですが、コメント文とトリッキーに闘わずともすみます。mattnさんに教えていただきました。ありがとうございました。

@more +1 "%~f0" | perl -S - %* & exit /b
# 以下スクリプト

ワンライナー

このメモの趣旨から離れますが、短いスクリプトなら-eオプションを使う方がコンパクトにすみます。
Ruby, Perl, Python, Gauche, GHCいずれでも使えます。

@インタプリタ -e "スクリプトの内容" %*

Goの場合は gore というツールが使えます。

バッチファイルからスクリプトファイルを呼び出す

このメモの趣旨からさらに離れますが、妥協案です。バッチファイルとスクリプト本体が別ファイルになります。外付けです。

元ファイルを編集せずにすむので、スクリプトがファイルとしてもともとある場合は、こういったbatファイルを別に用意するほうがトラブルが少ないかもしれません。

Goの場合

Goのスクリプトは go run foo.go で実行するのが常套手段なので、shebang的な使い方が想定されていません。
例はバッチファイルの拡張子をgoに変えたファイルを同じフォルダに置いた場合です。 (hoge.bat が hoge.go を呼び出す)

@go run "%~ndp0.go" %*

Pythonの場合

setup.pyなどでインストールされるPythonスクリプトの多くは、windowsではScriptsというフォルダに置かれます。こういったインストールずみのファイルを拡張子まで含めて書き換えるのは気が引けますね。

そこで、以下の様な内容のバッチファイルを用意します。%~n0は拡張子を除いたファイル名に展開されるので、例えばdumppdf.batというファイル名にすればdumppdf.pyを呼び出します。

@python "c:\Python27\Scripts\%~n0.py" %*

複数バージョンのPythonをインストールしている場合、直接Scriptフォルダに置けば、以下のようにバージョンに合ったPythonを呼び出すことができます。

@"%~dp0..\python.exe" "%~dp0%~n0.py" %*

一時ファイルを使う方法

バッチを起動するときに動作させるためのスクリプトファイルを解凍する方法です。

色々と危険ですが、掃除さえしっかりすればどんな言語でも適応できます。

JavaScriptの場合

一行コメントを使った例です。jrunscript -l js -fの部分をcscriptv8nodeに置き換えれば他のJavaScriptエンジンでも動かせます。また、一行コメントのある他の言語(coffeescriptやbashの#、 VBScriptの'など)にも応用できそうです。

@SET /P X=// < NUL > "%~dp0_TEMP_%~n0" & TYPE "%~f0" >> "%~dp0_TEMP_%~n0" & jrunscript -l js -f "%~dp0_TEMP_%~n0" %* & DEL "%~dp0_TEMP_%~n0"  & EXIT /B
// 以下スクリプトの内容

コメントでtatesukeさんに教えていただきました。ありがとうございました。元の例では一行コメントのない言語でも次のように対応できていました(REM */がミソ)。

@SET /P X=/* < NUL > "%~dp0_TEMP_%~n0" & TYPE "%~f0" >> "%~dp0_TEMP_%~n0" & jrunscript -l js -f "%~dp0_TEMP_%~n0" %* & DEL "%~dp0_TEMP_%~n0"  & EXIT /B & REM */
// 以下スクリプトの内容

おわりに

そもそもコマンド名補完がないから拡張子を邪魔に感じるわけで、PowerShellなりcygwinなりnyaosなりを使えばすむ話ですが他にshebangできそうな言語があれば教えてください。