shebangの怪

LinuxMac OS Xではshebangに書いたオプションの扱いが異なるらしい、という話。

ちょっと実験してみました。

#include <stdio.h>
main(int argc, char *argv[])
{
	int i;
	for (i = 0; i < argc; i++)
		printf("argv[%d] = %s\n", i, argv[i]);
}
#!./args a b c -d -e #abc
hoge

これで./test.shを実行してみる。まずLinuxでは、

argv[0] = ./args
argv[1] = a b c -d -e #abc
argv[2] = ./test.sh

という結果。引数が全て一つに纏められてしまっている。

一方Mac OS Xでは、

argv[0] = ./args
argv[1] = a
argv[2] = b
argv[3] = c
argv[4] = -d
argv[5] = -e
argv[6] = ./test.sh

という結果。引数は複数に区切られ、#以降は切り捨てられている。


以下にも解説がありますが、各OSでばらばらのようです。(なおMac OS Xの説明が上の実験結果と食い違っているようです。)
UNIXの部屋 コマンド:シェバング (*BSD/Linux)
いずれにせよ複数のオプションを書いてしまうと互換性がなくなるということですね。。。


perlrubyなどのインタプリタ型言語の場合、よくこういうオプションをshebangに書きますが、それがうまくいくのはshebangperl/rubyインタプリタが自分で読み取って動作を変えてくれているおかげだったのですね。


そういう賢い動作をしないコマンドの場合、そのコマンドをwrapするシェルスクリプトを書けば良いのですが、オプションだけのためにファイルをいちいち作るのは嫌なので、ファイルを一つで済ませられないか、と考えてみます。


標準入力から入力を食わせられる言語であれば、

#!/bin/sh
/usr/local/bin/some-interpreter -a -b -c <<EOF

%% 実際のコード

EOF

とhereドキュメントで流し込む方法が考えられます。
ただし、これだと 「some-interpreter -a -b -c スクリプト」として起動することはできません。


そこで、「 # 〜 」 も「 // 〜 」も1行コメント開始として扱ってくれる言語なら、

#!/bin/sh
//usr/local/bin/some-interpreter -a -b -c $0 ; exit

%% 実際のコード

という手もありかも。
//usr のようにディレクトリの区切りが複数になっても同じ意味であることを利用しています。
$0はシェルから見ると起動したファイル名になるので、それがスクリプトとしてオプション付きでインタプリタに引き渡されます。
後はインタプリタ側はshebangや//の行をコメントとして無視して実行してくれる、と。

これなら「some-interpreter -a -b -c スクリプト」としても動きます(当然オプションはコマンドラインで指定した方が有効なので、コマンドラインを追加したい時に便利)。


ちなみに、『「 # 〜 」 も「 // 〜 」も1行コメント開始として扱ってくれるがshebangのオプションは無視する言語』というのはSystemTapなんですが、オプションをいろいろ渡さなきゃならない (-D_HOGE_=1といったマクロ定義とか) ようなスクリプトを書いていると、テストするたびに毎回オプションを指定せねばならず、かつ時々オプションを変えたくなるので、デフォルト値をshebangに書いとけたらなあ、と考えていて思いついただけです。これはひどい