AWK で作る CGI

実践編


ランダムリンク

あらかじめ用意していたリンク先の中からデタラメに選んで 飛ぶというものを作りましょう。

素朴なスクリプト

非常に素朴な書き方をすると大体こんな感じです。


BEGIN{
 url_list[1]="http://member.nifty.ne.jp/Fe/"
 url_list[2]="http://ansermet.ad.kyushu-id.ac.jp/%7Ematsui/"
 srand()
 printf("Location: %s\n\n",url_list[int(rand()*2)+1])
}

笑ってしまうぐらい簡単ですが、一応解説しておきますと、 url_list という配列に飛びたいリンク先を指定しておいて、 それを乱数 (例によって区間(0,1)で発生する乱数の2(個数)倍の整数部分に1加えています) で選んで、CGI 頭書きの Location を使って指定しています。

実行してみたいですか? はい / いいえ

問題点としては、リンク先を増やそうと思うと、 スクリプト自体を書き換えなければならないということが挙げられます。 特に乱数を発生させるための個数を毎回書き換えるのは忘れてしまいそうです。 次は、もっとスマートな方法を考えましょう。

CGI の規格に書いてある注意書きによれば、REQUEST_METHOD が GET か HEAD でなければ Location を返してはいけないので、 その例外処理を書き加える方が好ましいでしょう。

データとスクリプトの分離

上で書いたように、スクリプトの中にデータを埋め込んでしまうと更新が大変です。 そこで、データは別ファイルに置くことにします。仮に urlfile としましょう。 スクリプトは以下のようになります。


BEGIN{
 for(i=1;getline < "urlfile" > 0;i++)
  url_list[i]=$0
 srand()
 printf("Location: %s\n\n",url_list[int(rand()*(i-1))+1])
}

urlfile には一行に一つだけリンク先が書いてあるとします。 また、それは少なくとも一つあるということも期待しています。 もう一つの仮定は、スクリプトがそのファイルを読めるということです。 これらの仮定を満たさないと、空の Location が生成されてしまいますので、 どうなってしまうか判りません。

awk に慣れていない方のために少しスクリプト自体について解説を加えます。

まず、for 文は c と同じで、

for(文1 ; 文2 ; 文3) 文4

が、

文1
while(文2){
    文4
    文3
}

という動作をします。文4は{}で囲まれた塊でも構いません。 (うるさいことを言うと、文2が空の時は恒真ですので while(1) に相当します)

getline は、 awk の入力を(暗黙のループを除いて)一手に引き受ける文なので、 いろいろな使い方があって憶えにくいのですが、 ここでの使い方に限って説明しますと、
getline < file
が file から1レコード (解らなければ1行だと思っていて下さい) 読み込む動作をしますが、その際、動作の成否によって値を返します。 具体的には、読み込みに成功すれば($0 にその値を格納して)1 を返し、 ファイルの終端に達したら 0 を、ファイルが読み込めなかったら -1 を返します。 したがって、上のスクリプトでは正常にファイルを読み込んでいる間は for ループを回るようになっています。 もし > 0 を忘れると、ファイルが読めないという状況 (権限を設定し忘れたとか、違うディレクトリーにあるとか…) で無限ループになってしまいますので注意しましょう(0 以外の値は全て真です)。

完成

以上の解説を踏まえて、例外に対処するように書き換えてみましょう。


BEGIN{
 request_method=ENVIRON["REQUEST_METHOD"]
 if(request_method!="GET"){
  printf("Status: 400 Bad Request\n\n")
  exit
 }
 url_list[0]="http://ring.etl.go.jp/"
 for(i=1;getline < "urlfile" > 0;i++)
  url_list[i]=$0
 srand()
 printf("Location: %s\n\n",url_list[int(rand()*i)])
}

最初に GET 以外の REQUEST_METHOD をはねます。 続いてデフォルトのリンク先を付け加えることで、 ファイルが空でも動作が正常に行なわれるようにします (このために乱数が区間[1,i)ではなくて[0,i)に変更されています)。


質問メールは大歓迎ですが、 必ずしもお答えできる自信はありません(苦笑)