ドラッグアンドドロップされたファイルをWSLのコマンドに渡すバッチファイル

小ネタ。

Windowsでちょっとした作業を自動化するのに便利なのがバッチファイルへのドラッグアンドドロップだ。 スクリプト言語はいろいろあるが、多くの場合コマンドラインからファイル名を指定して起動する必要がある。 それに比べるとドラッグアンドドロップはかなり直感的でお手軽である。

ファイルをバッチファイルにドラッグアンドドロップすると、ファイルをそのバッチファイルの引数にとってバッチファイルが実行されるので、普通にWindowsのコマンドにファイルを渡すのは簡単だ。 ただ、これを使ってWSL(Windows Subsystem for Linux)のコマンド(WSLにインストールしたソフト)にファイルを渡すのは、パスの変換の問題があり、少し面倒である。 ここではその方法について解説する。

例:ffmpegで音楽をmp3に変換する

WSLにインストールしたffmpegで、ドラッグアンドドロップされた音声ファイルをmp3に変換するバッチファイルを書く。

WSLのコンソールからなら

ffmpeg hoge.wav -ab 192k hoge.mp3

と書くものを、どうやってバッチファイルで自動化するかという問題だ(hoge.wavが入力するファイル、hoge.mp3が出力先のファイル名である)。

とりあえず試した限りうまくいっていそうなのが、次のようなバッチファイルだ。

chcp 65001
set filename1=%1
set filename2="%~dp1%~n1.mp3"
for /f "usebackq delims=" %%a in (`wsl wslpath -a %filename1:\=/%`) do set wslname1=%%a
for /f "usebackq delims=" %%a in (`wsl wslpath -a %filename2:\=/%`) do set wslname2=%%a
wsl ffmpeg -i "%wslname1%" -ab 192k "%wslname2%"

一目見て狂っているようにしか見えないが、これがバッチファイルの世界らしい。 軽く解説を書いておく。

chcp

これは文字コードの変更。

普通にインストールした日本語Windowsコマンドプロンプト文字コードはコードページ932(≒シフトJIS)になっている。バッチファイルもその文字コードで動くらしい。 このあとwslpathというコマンドでWindowsのパスをwslのパスに変換するのだが、このまま使うと文字化けする。

chcp(change code page)コマンドでコードページ65001(UTF-8)に変更するとうまくいくようになる。

ファイル名の取得

set filename1=%1は、第一引数(ドロップされたファイル名)の変数への代入。

set filename2="%~dp1%~n1.mp3"は、気持ち悪いが、第一引数となったファイルの拡張子をmp3に変更したものを変数に代入している。 ちなみにこのダブルクオートは大事。このダブルクオートはそのままfilename2に代入されるのだが、これがないとファイル名にスペースを含むファイルを渡したときに、あとのところ(wslpathに渡すところ)でバグる。

www.ne.jp

WSLパスへの変換

バッチファイルではコマンド実行結果の取得が、Linuxのシェルのようにバッククオートで囲むだけで簡単にできるようなことはない。

wsl wslpath -a %filename1:\=/%の結果をwslname1という変数に代入するためにfor /f "usebackq delims=" %%a in (`wsl wslpath -a %filename1:\=/%`) do set wslname1=%%aと書く必要がある。

このへんの解説は次のQiitaの記事を読むのがよさそう。

qiita.com

wslpathはWindowsのパスとWSLのパスを相互に変換するコマンドだが、これ自体WSL側のコマンドなので、コマンドプロンプトから呼び出すときはwsl wslpath -a <Windowsのパス>のようにする。

ただ、Windowsのパスをそのまま渡すと\の文字がLinuxのシェルのエスケープ文字と解釈されて死ぬ。 回避のために、ファイル名は%filename1%とそのまま渡すのではなく、%filename1:\=/%のようにして\/に置換してから渡す。

web.plus-idea.net

ffmpegの起動

ここまで来ればもう怖いことはない。 wslname1とwslname2に入出力するファイル名が入っているので

wsl ffmpeg -i "%wslname1%" -ab 192k "%wslname2%"

と素直に書けば良い。

これで、ドラッグアンドドロップで簡単にmp3に変換できるようになる。出力ファイルはもとの音声ファイルと同じディレクトリになるので、このバッチファイルをデスクトップにでもおいておけば、どこのフォルダにあるファイルも良い感じに変換できるだろう。

これをコピペ改変すればffmpeg以外、例えばimagemagickで画像を縮小してサムネイルを作るようなことも簡単だ。

複数ファイルのドラッグアンドドロップに対応させる

上の例だと1ファイルにしか対応できないが、複数のファイルをドラッグアンドドロップし、それぞれに同じ処理をしたい場合は次のように書けば良い。

chcp 65001
:loop
if "%~1" == "" goto end
set filename1=%1
set filename2="%~dp1%~n1.mp3"
for /f "usebackq delims=" %%a in (`wsl wslpath -a %filename1:\=/%`) do set wslname1=%%a
for /f "usebackq delims=" %%a in (`wsl wslpath -a %filename2:\=/%`) do set wslname2=%%a
wsl ffmpeg -i "%wslname1%" -ab 192k "%wslname2%"
shift
goto loop
:end

shiftコマンドでコマンドライン引数が1つ前に移動するので、これとgotoを組み合わせることで順次処理ができるのである。

qiita.com

その他の方法

見ての通り、バッチファイルは難しい。他の方法として次のようなものがある。

RubyPythonスクリプトドラッグアンドドロップで起動できるようにする

実は可能である。実にまともな解決策ではあるが、レジストリを編集する必要があり、私は面倒でやりたくないなぁと思ってしまう。

そして結局WSLのコマンドに渡すためにはwslpathを使う必要があり、文字コード周りなんかの苦労は変わらなそうに思える。

tipszone.jp

それよりは、(2ファイルにはなるけれど)このバッチファイルの最後のところでwsl ruby hoge.rb %wslname1%とかしたほうがいいかなという気持ちだ。これだとWSL側のRubyPythonを呼べるわけだし。

ショートカットを使う

……と思っていたのだが、ショートカットを使えば、(2ファイルにはなるけれど)ドラッグアンドドロップでのスクリプトの起動がレジストリ編集なしでできるらしい。

misohena.jp

これでRubyあたりのスクリプトを起動させるとお手軽かもしれない。

Rubyで試したところ、ショートカットのリンク先を素直にruby hoge.rbにしておくだけで、ドラッグアンドドロップされたファイルをコマンドライン引数としてhoge.rbを実行してくれるようだ。 「作業ディレクトリ」には注意。

WSFを使う

教えてもらうまで完全に存在を忘却していたのだが、ドラッグアンドドロップから起動できるものにはWSF(Windows Script File)というのがある。

VBScriptJScriptを書けるなら、バッチファイルよりはマシかもしれない。私は書けないけれど。

2020-09-25 追記: 続編→ ドラッグアンドドロップされたファイルをWSLのコマンドに渡すバッチファイル - その2 - すずしめにっき