msys上だとRubyのワイルドカード展開がやっかい
あるRubyのスクリプトで、オプションとしてワイルドカードを利用した指定をできるようにしようとしてみたのですが、僕の期待に反してうまく動いてくれませんでした。
UNIX等のシェル上において通常、ワイルドカード指定をした場合はシェルによって展開されます。しかし、Windows上ではシェルが展開をしないため、Windows上でRubyを動かすときはRubyがワイルドカードの展開を行います。
ワイルドカード展開をさせたくない時は、シングルクォテーションで括ってやればOKです。…OKな、はずでした。
ところがどっこい、これがうまく動いてくれないんです。シングルクォテーションで括っても、ワイルドカード展開されてしまいました。試行錯誤したところ、シングルクォテーションで括ってからさらにそれをダブルクォテーションで括ると、ワイルドカード展開されないことがわかったのですが、さすがにこれは面倒です。
なんとかならないかと調べてみたところ、どうやらmsysがまずいらしいということがわかりました。具体的には、スクリプト呼び出し時に付けたクォートが、プロセス呼び出し時にははずされているのです。Rubyのソースファイル「win32.c」のNtInitializeあたりでGetCommandLine()で取得できるコマンドラインを出力するとよくわかります。
ruby -e 'p ARGV' '*.c' -c -h
このワンライナーを実行すると、msys上からか、コマンドプロンプト上からかで出力が異なります。
<msysの場合の出力> c:\usr\local\bin\ruby.exe -e "p ARGV" *.c -c -h # GetCommandLine()で取得できるコマンドラインの中身 ["hoge.c", "hogehoge.c", "-c", "-h"] # ワンライナーの実行結果 <コマンドプロンプトの場合の出力> c:\usr\local\bin\ruby.exe -e 'p ARGV' '*.c' -c -h # GetCommandLine()で取得できるコマンドラインの中身 ["*.c", "-c", "-h"] # ワンライナーの実行結果
msys上からだと、rubyのプロセスに渡される '*.c' のシングルクォートが外れているのがわかると思います。クォートが外れた状態でrubyに渡されるので、rubyは独自にワイルドカード展開を行ってしまうというわけです。
どう対処しようか。たっぷり悩んだ結果、次のように独自に加工してからオプションをrubyに渡すようにすることにしました。
#!/bin/sh for elm in "$@" do args[$i]="'${elm}'" i=$i+1 done exec ruby -S -x $0 "{args[@]}" #! ruby p ARGV
msysが勝手にクォートを外すならば、もう一度こちらで付け直して、直接渡してしまえというもくろみです。実際はexec経由でrubyを立ち上げている以上、同様のことが起きて再びクォートを外すのではないかと思うのですが、うまくいってしまいました。う〜ん、いまいち納得がいきませんが、とりあえずこれで煮詰まっていたツールの機能を拡張できます。