ドラッグアンドドロップされたファイルをWSLのコマンドに渡すバッチファイル - その2
以前書いた記事の補足というかおまけというか。
前回の記事の「その他の方法」のところで、RubyやPythonのスクリプトをドラッグアンドドロップで起動するためにはレジストリを弄るか2ファイルに分ける必要があると書いた。
しかし、1ファイルかつレジストリを弄ることなく、ドラッグアンドドロップされたファイルを引数に取るRuby等のスクリプトを起動する方法があることを知った。というわけで、この方法を追記しておく。
Rubyの場合
早速結論を示そう。
前回も取り上げた、ドラッグアンドドロップされたファイルをffmpegでmp3に変換するスクリプトをRubyで書いたものが以下である。
chcp 65001 cd /d %~dp0 ruby -x %0 %* exit /b 0 #!ruby # 与えられた全入力ファイルに対してループ ARGV.each do |infile| # ファイルのwslでのパスを取得 infile_wsl = `wsl wslpath -a '#{infile.gsub(/\\/, '/')}'`.chomp # 拡張子をmp3に変えたものを出力ファイル名にする outfile_wsl = "#{File.dirname(infile_wsl)}/#{File.basename(infile_wsl, ".*")}.mp3" # ffmpegを実行 system("wsl ffmpeg -i '#{infile_wsl}' -ab 192k '#{outfile_wsl}'") end
これを 拡張子batで(文字コードはUTF-8)保存してやると、ドラッグアンドドロップされたファイルを引数として実行できるファイルが完成する(なお、RubyがWindows側にインストールされていて、ruby
とコマンドプロンプトで入力すれば呼び出せる状態になっている必要がある)。
え? 「Rubyのスクリプトじゃなくてバッチファイルじゃないか」って?
……これが今回紹介する手法である「Rubyスクリプトとしても実行できるバッチファイル」である。
要するに「バッチファイルからRubyスクリプトを起動する方法だと2ファイルになってしまうけど、じゃあRubyスクリプトを自分自身に書いてしまえば1ファイルで済むよね」と、そういう話だ。
別にこれは私が考えたものではなくて、「複数のプログラミング言語として解釈可能なプログラム」は polyglot という名で呼ばれている*1。
「超絶技巧プログラミング」とか言われているような非実用的なお遊びプログラミングの方面の手法という印象が強かったのだけれど、こういう風に実用的に役立つ場面もあるのである。
さて、上に貼ったWikipediaにはPerlとしても実行できるバッチファイルとして
@rem = ' --PERL-- @echo off perl "%~dpnx0" %* goto endofperl @rem '; #!perl print "Hello, world!\n"; __END__ :endofperl
が紹介されているが、似たことをするにしても私の記したRubyの例は少し黒魔術感が減っていると思う。
これは ruby
の -x
オプションという便利なもののおかげである。
-x[directory]
メッセージ中のスクリプトを取り出して実行します。スクリプトを読み込む時に、
#!
で始まり, "ruby"という文字列を含む行までを読み飛ばします。スクリプトの終りはEOF
(ファイルの終り),^D
(コントロールD),^Z
(コントロールZ)または予約語__END__
で指定されます。
これで前半のバッチファイル部分は読み飛ばしてくれるから、Wikipediaの例のように頑張って文字列リテラルに見せかけたりする必要はないのである。
Perlの場合
ruby
の -x
オプションとほぼ同じ働きをする -x
オプションがPerlにもある。というかRubyのオプションはおそらくPerl由来である(要出典)。
なお、ドキュメントには
-x
-xdirectoryメールのような大きな無関係のテキストのかたまりの中にプログラムが 埋め込まれている事を Perl に伝えます。 最初の #! で始まり、"perl" という文字列を含む行までの、先行するゴミは 捨てられます。 その行にある意味を持つスイッチは適用されます。
(後略)
とある。 polyglotは後から出てきた使い方で、この機能は本来メールでプログラムやパッチをやりとりしていた時代の遺物なのだろう。
面倒なのでmp3変換の例は書かないけれど、ほとんどRubyと同じ使い方ができるはずだ。
Pythonの場合
Pythonの場合も同様のことはできるのだが、事情は少しだけ異なる。
-x
Unix 以外の形式の #!cmd を使うために、ソースの最初の行をスキップします。これは、DOS専用のハックのみを目的としています。
とのことで、最初の1行しか読み飛ばしてくれない。
だが対応策は単純で、バッチファイル部分を1行に収めればよい。
chcp 65001 & cd /d %~dp0 & python -x %0 %* & exit /b 0 #!python print("Pythonから話しかけています") import time time.sleep(5)
シェルスクリプトでの;
に相当するコマンドの連結が、バッチファイルでは&
でできるので、それを使って改行をなくしてやるだけである。
結語
以上、バッチファイルとして各種軽量プログラミング言語のスクリプトを実行する方法を紹介した。古典的な方法であるが、バッチファイルで苦労する機会が減らせるかもしれない。
なお、これと同様のことはバッチファイルではなくLinuxのシェルスクリプトでも可能だが、シェルスクリプトならヒアドキュメントを使ってもほぼ同じ事が実現可能である(そしてヒアドキュメントのほうが素直だと思う)。
*1:polyglot の本来の意味は「多国語に通じた人」「多国語で書かれた書物」等。