2008年4月5日土曜日

/usr/bin/perl: bad interpreter: No such file or directory

これは、Linux系なOS、とくにFedora Coreで見かけたエラーメッセージなんですが、

/usr/bin/perl: bad interpreter: No such file or directory

というのをネット検索すると、「ファイルの改行コードが違うのが原因」という回答がよくでてきます。



以下は、ちょっとFreeBSDで試してみた例です。



こんなperlスクリプトがあるとします。

% cat aaa.pl
#!/usr/bin/perl



print "hello world\n";

実行してみます。

% chmod +x aaa.pl
% ./aaa.pl
hello world

正常に動いてますよね。



「nkf --msdos」で、改行コードを、MS-DOS形式のCR LF(\0d \0a)にしてみます。

% nkf --msdos < aaa.pl > bbb.pl

ダンプして確認。0d 0aになってます。

% hd bbb.pl
00000000  23 21 2f 75 73 72 2f 62  69 6e 2f 70 65 72 6c 0d  |#!/usr/bin/perl.|
00000010  0a 0d 0a 70 72 69 6e 74  20 22 68 65 6c 6c 6f 20  |...print "hello |
00000020  77 6f 72 6c 64 5c 6e 22  3b 0d 0a                 |world\n";..|
0000002b

実行してみます。

% chmod +x bbb.pl
% ./bbb.pl
./bbb.pl: コマンドが見つかりません.

エラーにはなりましたが、エラーメッセージがぜんぜん違いますよね。でもですね、いや、あの~、./bbb.plはあるんですけど、見つかりませんってどういうこと?



このエラーメッセージって、実は、シェルが出しているものらしくて、今、たまたま、シェルがtcshだったので、「コマンドが見つかりません.」になっています。



そこで、bashを実行して、bashの上で、bbb.plを実行してみます。

% bash
[ttt@FreeBSD ~]$ ./bbb.pl
bash: ./bbb.pl: /usr/bin/perl^M: bad interpreter: No such file or directory

「bad interpreter: No such file or directory」が出ました。
そう、bashが出してるメッセージだったんですね。



「^M」と表示されていますが、これは、文字コード0x0dのことです。0x0dは、10進数で13ですが、A、B、C、…を、1、2、3、…と数えていくと、13番目がMなので、「^M」なんです。



emacsやviなどのテキストエディタでbbb.plを開くと、行末に^Mがついているのを確認できると思います。



ちなみにですが、FreeBSDの/bin/shで試したら

% /bin/sh
$ ./bbb.pl
./bbb.pl: not found

でした。う~ん、微妙。bad interpreterって、bashだけですか?





実は、こんなことがおきてます。


  1. シェルに対して、ユーザーが./bbb.plを実行しろ、と指示をする


  2. シェルは、./bbb.plのファイルの先頭を読み出す


  3. 「#!」と書いてあった場合、そのあとに書かれているコマンドを実行する(厳密には、このへんのカラクリには、もっといろいろあるらしい)


  4. そのあとに書かれているのは、「/usr/bin/perl」だったらそれを実行するんだけど、改行コードが、MS-DOS形式の0x0d 0x0aとなっている場合が問題。


  5. UNIXでは、改行コードは0x0aだけなので、0x0dがあまってる。「/usr/bin/perl」に0x0dまでくっつけた、「/usr/bin/perl^M」までをコマンド名だと思って、実行しようとする。


  6. でも、「/usr/bin/perl^M」は無いので、エラーになる、と。

以上のようなことなので、改行コードが間違っている以外の場合でも、たとえば、「#!/usr/bin/pearl」と書いてしまったとしても

[ttt@FreeBSD ~]$ ./bbb.pl
bash: ./bbb.pl: /usr/bin/pearl: bad interpreter: No such file or directory

とエラーになります。



・・・まあ、ここまでは、よくある話。





私、GridEngineというソフトウェアを使って、多数のホストで、大量のプログラムを並列実行させることがあるのです。



ホストには、いろんなOS、いろんなCPUアーキテクチャ、いろいろあります。



プログラムは/bin/shのシェルスクリプトにしてあり、そこからperlスクリプトを実行したり、その他バイナリプログラムの場合でも、アーキテクチャの違いを気にしなくてもいいように、環境を整えてあります。



これで、とってもうまいこと動いてくれているのですが、なぜか、Linuxでだけ、ごくごくまれに、だいたい数百~数千回に1回程度の低い確率で、

/usr/bin/perl: bad interpreter: No such file or directory

というエラーがでるのです。たまに、微妙に違うメッセージになったりもしてるのですが、No such file or directoryは共通でした。



これを、ちょっと調べてみました。怪しいのは、automountなんじゃないか?と思って。



たとえば、automountによって、/host/server01/が自動的にNFSマウントされるようになっていたとします。



/host/server01/script/aaa.pl という名前で、以下のようなperlスクリプトをおきます。


#!/usr/bin/perl



for ( $i = 0; $i <20; $i ++ ) {
    print $i . "\n";
    system("mount | grep server01");
    sleep 10;
}


あるLinuxマシンで、/host/server01/script/aaa.pl を実行します。



実行しようとした瞬間/host/server01がNFSマウントされますが、60秒ほど経過すると、自動的にアンマウントされます。



このautomountが自動的にアンマウントするタイミングって、正確にはいつなんでしょうね?マニュアルを読んでもよくわかんなかったのですが・・・



タイムアウト時間が設定できるようになっているんですが(Fedoraなどでは、デフォルトが60秒になっているようです)、タイムアウトって、いつから計ってるんですかねぇ?



マウントしたときから? そのファイルシステムへ最後にアクセスしてから?
後者だという気はしますけど・・・



GridEngineで実行しているシェルスクリプトは、/host/server01/とはぜんぜん別のところにあるのですが、そのシェルスクリプト中に、/host/server01/script/aaa.plを実行しようとします。



ここからはまったくの予想なんですが・・・



NFSのキャッシュかなんかで、/host/server01/script/aaa.pl の先頭に、「#!/usr/bin/perl」と書かれていることだけは、なぜかわかっていて、aaa.plの後半を本格的に読み出そうとしたときに、偶然、automountによって、アンマウントされているんじゃないか?



そうだとすれば、偶然たまたま、すごく低い確率でエラーがでるのも、納得。



ただ、なんとなく、つじつまがあわないんですよね。



aaa.plの「#!/usr/bin/perl」を読み出した時点で、タイムアウト時間が先延ばしになって、アンマウントされるのって、おかしくない?



aaa.plの「#!/usr/bin/perl」を読み出せていたんなら、/usr/bin/perl: bad interpreter: No such file or directory じゃないんじゃない?



という感じ。ただ、perlの実行が開始したあとに、aaa.plが読めないというエラーも、まれに発生してたような、してなかったような・・・


回避方法として、こんなのを考えてみました。



シェルスクリプトの中で、こんなの書き足してみました。

{ cd /host/server01/script/; sleep 1200 & }

NFSマウント先をカレントディレクトリとするプロセスがいれば、自動的にアンマウントされないでしょうからね。



これで、しばらく様子を見てみようと思います。



0 件のコメント:

コメントを投稿