これは、Linux系なOS、とくにFedora Coreで見かけたエラーメッセージなんですが、
/usr/bin/perl: bad interpreter: No such file or directory
というのをネット検索すると、「ファイルの改行コードが違うのが原因」という回答がよくでてきます。
以下は、ちょっとFreeBSDで試してみた例です。
こんなperlスクリプトがあるとします。
% cat aaa.pl
#!/usr/bin/perlprint "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だけですか?
☆
実は、こんなことがおきてます。
- シェルに対して、ユーザーが./bbb.plを実行しろ、と指示をする
- シェルは、./bbb.plのファイルの先頭を読み出す
- 「#!」と書いてあった場合、そのあとに書かれているコマンドを実行する(厳密には、このへんのカラクリには、もっといろいろあるらしい)
- そのあとに書かれているのは、「/usr/bin/perl」だったらそれを実行するんだけど、改行コードが、MS-DOS形式の0x0d 0x0aとなっている場合が問題。
- UNIXでは、改行コードは0x0aだけなので、0x0dがあまってる。「/usr/bin/perl」に0x0dまでくっつけた、「/usr/bin/perl^M」までをコマンド名だと思って、実行しようとする。
- でも、「/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マウント先をカレントディレクトリとするプロセスがいれば、自動的にアンマウントされないでしょうからね。
これで、しばらく様子を見てみようと思います。