簡単なページを出力してみる

まずは、簡単な HTML によるページを表示する CGI プログラムを作ってみます。
lighttpd を導入した時に、以下のような簡単なテスト用 CGI プログラムを作りました。

#!/usr/local/bin/perl

print "Content-Type: text/html\n\n";
print "Hello, world!\n";

この CGI プログラムをブラウザで開くと、ただ単に「Hello, world!」と表示されるだけですが、このメッセージの代わりに HTML 文を出力する事で、ページを表示する事ができます。

#!/usr/local/bin/perl

print "Content-Type: text/html\n\n";
print <<HTML;
<html>
  <head>
    <title>Hello, CGI world!</title>
  </head>
  <body>
    <h1>Hello, CGI world!</h1>
    <p>Welcome to CGI world! This is a test page.</p>
  </body>
</html>
HTML
# "HTML" の後ろに改行が必要です

この CGI プログラムは、簡単なページを出力します。HTML については詳しい説明を省略しますが、わからない部分はお気軽にコメントなどでお尋ねください。
hellocgi.cgi 実行結果
上のリンクは、ぱるも砂場内のページへ移動します。広告はサーバーによって自動的に挿入されたものです。以降 CGI の実行結果は、ぱるも砂場内のページにて行なう予定です。
Hello, CGI world! というページが表示されました。簡単ですね。(^_^)


ところで、CGI プログラムを UNIX 系のウェブサーバー上で実行させる場合、パーミッションの設定が必要です。パーミッションとは「許可」という意味で、「ファイルのオーナー」「グループ内のユーザー」「その他のユーザー」それぞれに対して、「読み込み」「書き込み」「実行」の許可/不許可を、ファイルごとに行なう事ができます。
「実行」に対する許可は通常はオフになっているはずですので、CGI を実行する為には「実行」に対する許可が必要です。どのようなパーミッションにすれば実行できるようになるかは、サーバーによって異なるので一概には言えませんが、「755」(rwxr-xr-x)や「705」(rwx---r-x)などが多いように思います。
パーミッションの設定は FFFTP などの FTP クライアントソフトを使えば簡単にできます。(^_^)

配列やハッシュを HTML で出力する

先ほどのエントリでは HTML 文によりページを表示しましたが、ただ単に HTML 文を print しているだけだったので、これでは HTML ファイルを表示してるのと、なんら変わりません。
そこで、今度は変数を埋め込んでページを表示してみます。スカラーは p 要素として、配列は ol 要素として、ハッシュは dl 要素として表示してみたいと思います。
関数に切り分けたら、ちょっと長くなりましたが、こんな感じになりました。

#!/usr/local/bin/perl

use strict;

sub print_array(\@) {
    print "<ol>\n";
    print "<li>$_</li>\n" for (@{ $_[0] });
    print "</ol>\n";
}
sub print_hash(\%) {
    my $h = shift;
    print "<dl>\n";
    print "<dt>$_</dt><dd>$h->{$_}</dd>\n" for (keys %$h);
    print "</dl>\n";
}
sub print_var {
  while (my $v = shift) {
    my $type = ref($v);
    if    ($type eq '')       { print "<p>$v</p>\n"; }
    elsif ($type eq 'SCALAR') { print "<p>$$v</p>\n"; }
    elsif ($type eq 'ARRAY')  { print_array(@$v); }
    elsif ($type eq 'HASH')   { print_hash(%$v); }
    elsif ($type eq 'REF')    { print_var($$v); }
    else                      { print "<p>$type</p>\n"; }
  }
}

# メイン処理
{
    print "Content-Type: text/html\n\n";
    print <<HEADER;
<html>
<head><title>Printing Variables</title></head>
<body>
HEADER

    my $sc = "Hello, my name is Palmo!";
    my $ar = ["First", "Second", "Third"];
    my $ha = {Apple => "Red", Banana => "Yellow", Peach => "Pink"};

    print_var($sc, $ar, $ha);

    print <<FOOTER;
</body>
</html>
FOOTER
}

print_array 関数は、渡された配列を ol 要素として出力します。同じく print_hash 関数は渡されたハッシュを dl 要素として出力します。これらの関数はプロトタイプによってリファレンスを受け取るようになっています。リファレンスをやりとりする事で、無駄なコピーを抑える事ができます。
print_var 関数は、渡された値のリストをそれぞれ HTML で出力します。ref 関数スカラー変数の中身がリファレンスじゃないなら空文字を、リファレンスなら「何」を参照しているのかを表した文字列を返してくれるのでしたね。これを利用して、リファレンスが渡された時も、中身を適切に表示できるようにしました。if 文が羅列していて、ちょっと汚いですね……。(^_^;)
メイン処理でページを表示しています。$sc はスカラー値、$ar は無名配列へのリファレンス、$ha は無名ハッシュへのリファレンスがそれぞれ入りますね。これを print_var 関数に渡すと、ちゃんと表示してくれるはずです。
var2html.cgi 実行結果
上手く表示できました。(^_^)


ところで if 文の羅列が嫌な時は「Switch」モジュールを使えば良いそうです。このモジュールを use すると、C言語などでお馴染みの switch 構文が使えるようになります。
Switch モジュールを使って上のスクリプトの print_var 関数を書き換えると、以下のようになります。

use Switch;

sub print_var {
  while (my $v = shift) {
    switch (my $type = ref($v)) {
        case ''        { print "<p>$v</p>\n"; }
        case 'SCALAR'  { print "<p>$$v</p>\n"; }
        case 'ARRAY'   { print_array(@$v); }
        case 'HASH'    { print_hash(%$v); }
        case 'REF'     { print_var($$v); }
        else           { print "<p>$type</p>\n"; }
    }
  }
}

if 文を羅列するよりは、断然読みやすいですね。
Switch モジュールによる switch は、C言語の switch よりも優れていて case 文には数値に限らず色々な条件を書く事ができます。上のように文字列による case も可能ですし、リストやハッシュなどを書けばその中に switch 対象が存在するかどうか調べられ、正規表現を書けばマッチングが行なわれ、コードブロックを渡せば対象を引数として実行されてその返り値が調べられます。
何でもありですね。(^_^;)

ぱるも砂場を作りました

palmo2006-07-08

CGI の実行結果や、ソースコードなどが簡単に見られるように、新たに「ぱるも砂場」というサイトを立ち上げました。(まだ仮オープンです)
PerlCGI に関するまとまった勉強メモや、各種スクリプト・プログラムのダウンロードなどは、こちらで公開していこうと思います。もちろん、勉強日記やプログラムの製作過程などは、ぱるも日記で引き続き書いていきますので、よろしくお願いします。
ちなみに、ぱるも砂場land.to さんの無料レンタルサーバーで運営しています。100 MB、CGIPHP、商用利用可など、無料とは思えない素晴らしいサーバーです。(^_^)
はてな といい、数々のサービス、ソフトウェアが無料で利用できるようになり、とても嬉しく、ありがたく思います。感謝。

再開します

ここ一週間、多忙につきお休みしてました。余裕がでてきたので、そろそろ再開したいと思います。
これからどちらへ進むか悩んでいましたが、CGI を勉強しつつ、有名なモジュールの使い方を勉強していく方向に行きたいと思います。その合間に、取りこぼした基礎も勉強しようかなぁ、と。
行き当たりばったりですが、よろしくお願いします。(^_^)

Windows で lighttpd を使う

CGI をローカルでテストする為の環境を整えたいと思います。
サーバーソフトウェアには lighttpd (別名 lighty)を使ってみます。もちろん Apache でも良かったのですが、お手軽で速いという噂の lighttpd を試してみたいと思っていたので。(^_^)
lighttpd は各種プラットフォームで動作します。Windows 向けのバイナリは、Kevin Worthington さんのサイトにて配布されています。本来は Cygwin という Windows 用の UNIX ライクな環境の導入が必要でしたが、こちらのサイトで配布されているパッケージを使う場合は、必要ありません。

インストール

今回は lighttpd 1.4.11 をダウンロード・インストールしました。ダウンロードしたインストーラを起動し、「Next」ボタンを押していくだけでインストール完了です。インストール先は「C:\lighttpd」で固定のようです。とても簡単。(^_^)
インストール完了時に「lighttpd を起動しますか?」と聞かれたので、起動してみました。試しにブラウザから http://localhost/ を開くと、「lighttpd Test Page」が表示されます。インストールは上手く出来たみたいですね。また、lighttpd の開始・停止はスタートメニューから簡単に出来ます。

設定

次に、設定ファイルを編集して lighttpd を設定します。設定ファイルは通常「C:\lighttpd\etc\lighttpd.conf」となります。
以下はあくまで palmo 自身が行なった設定ですので、参考になさる場合はお使いの環境に合わせて設定内容を変えてください。また、行番号は「lighttpd.conf v1.7」でのものです。


まずは読み込むモジュール。「server.modules」です。

## modules to load
# at least mod_access and mod_accesslog should be loaded
# all other module should only be loaded if really neccesary

和訳すると「最低でも mod_access と mod_accesslog は読み込まれるべきですが、その他の全てのモジュールは本当に必要な時だけ読み込まれるべきです」でしょうか。mod_rewrite や mod_auth など、モジュールの名前が Apache と同じなので、馴染みやすいですね。
CGI を有効にするには mod_cgi モジュールを読み込む必要があります。lighttpd.conf の server.modules の中から 29 行目の

#                               "mod_cgi",

という行を、先頭の # を消して有効にします。

                                "mod_cgi",

これで mod_cgi モジュールが有効になるはずです。


さらにその下 40 行目には、「server.document-root」があります。ここで指定したフォルダに http://localhost/ からアクセスできるようになります(ドキュメントルートとなる)。デフォルトでは「C:/lighttpd/htdocs」となっていますが、自由に変更しても良さそうです。「D:\home\palmo\htdocs」に設定してみました。バックスラッシュ(円マーク)は、スラッシュにした方が良さそうですね。

server.document-root        = "D:/home/palmo/htdocs/"


46 行目の「index-file.names」は、リクエストされた URL が「/」で終わっていた時にデフォルトで読み込むファイルですね。恐らくリストの最初にあるファイル名が優先されると思われます。一応、「index.cgi」を先頭に加えておきます。

index-file.names            = ( "index.cgi", "index.php", "index.html",
                                "index.htm", "default.htm" )

これで、例えば http://localhost/ を開いた時に http://localhost/index.cgi があれば自動的に開かれるようになりました。


133 行目の「static-file.exclude-extensions」は、説明によると「静的ファイルとして処理しない拡張子」です。最初から ".pl" や ".fcgi" (Fast-CGI 用の拡張子)が登録されていますが、ここにも一応 ".cgi" を登録しておきます。(もしかしたら必要ないかも)

static-file.exclude-extensions = ( ".php", ".pl", ".fcgi", ".cgi" )


標準では lighttpd はポート 80 で動きますが、他のサーバーソフトウェアなどが既にポート 80 を利用している場合は、競合してしまいます。その場合は 138 行目の「server.port」を有効にして、好きなポート番号にすれば良いですね。(^_^)
例として、ポートを 81 に変えた場合は http://localhost:81/lighttpd サーバーにアクセスできるようになります。


219 行目からが CGI モジュールの設定です。といっても設定はとても簡単。拡張子と実行プログラムの対応関係を設定するだけです。

#cgi.assign                 = ( ".pl"  => "/usr/bin/perl",
#                               ".cgi" => "/usr/bin/perl" )

まず、先頭の # を消して有効にします。次に "/usr/bin/perl" を perl.exe へのパスに書き換えればいいみたいです。Apache とは違い Shebang 行から自動的に読み込んでくれるわけではないようですね。
僕の場合 Perl は「C:\Perl」へインストールしたので perl.exe のパスは「C:\Perl\bin\perl.exe」となっています。詳しくは後述しますが、Apache を使っていたら CGI プログラムをアップロードする前に Shebang 行を書き換える必要があったので、lighttpd のこの機構はとても助かります。
perl.exe へのパスのバックスラッシュをスラッシュに書き換えて、こうなりました。

cgi.assign                 = ( ".pl"  => "C:/Perl/bin/perl.exe",
                               ".cgi" => "C:/Perl/bin/perl.exe" )

これで拡張子 .pl や .cgi のファイルは perl.exe で実行されるようになるはずです。


次は簡単なセキュリティの設定をします。インターネットにつながっているコンピュータ上でサーバーを立てる場合、外部から接続できるので注意が必要です。自分のグローバルIPアドレスがわかる場合は http://IPアドレス/ をブラウザで開くと確認できます。
僕の場合、lighttpd はローカルでのテスト専用のサーバーとして使うので、外部からアクセスできないようにしたいと思います。(といってもファイアーウォールで弾けるとは思いますが、念の為)
といっても、デフォルトの設定ファイルではそれらしき設定が見つからなかったので、マニュアル(C:\lighttpd\doc\configuration.txt)を読んでみました。どうやら Perl のような条件式を使って設定が書けるようです。これはわかりやすいですね。(^_^)
ローカルから見る場合 $HTTP["remoteip"] に "127.0.0.1" が入っています。mod_access モジュールのと組み合わせて、こんな感じになりました。

$HTTP["remoteip"] != "127.0.0.1" {
  url.access-deny = ("")
}

これを lighttpd.conf の末尾に追加しておきます。
url.access-deny は、指定した文字列を末尾に持つファイルへのアクセスを禁止できます。空文字を指定したので、全てのファイルへのアクセスを禁止しています。

  url.access-deny = (".inc", ".log")

のようにすれば、拡張子が「.inc」か「.log」のファイルへのアクセスが禁止になります。シンプルですね。


これで大体の設定ができました。シンプルな設定ファイルなので、設定がとてもわかりやすいです。個人で使うのにうってつけですね。lighttpd は大規模サイトの運用にも耐えるそうなので、設定ファイルの書き方を覚えておけば今後の役に立ちそうです。
設定ファイルを保存したら lighttpd を再起動すれば反映されます。

試してみる

設定したドキュメントルート(僕の場合は D:/home/palmo/htdocs/)に適当な index.html を置いて、http://localhost/ を表示してみると、ちゃんと確認できました。(更新が必要かもしれません)


今度はいよいよ CGI が動くか確認します。簡単な CGI スクリプトを書いてみました。

#!/usr/local/bin/perl

print "Content-Type: text/html\n\n";
print "Hello, world!\n";

1行目は「Shebang行」(シーバン行)と呼ばれるもので、これについては別のエントリで詳しく説明したいと思います。
1つめの print 文は、訪問者のリクエストに対する返事(レスポンス)のヘッダーに出力する「Content-Type」です。これは何かと言うと、このプログラムが今から「何を」出力するかを知らせています。CGI プログラムは色々なものを「出力」する事ができるので、何を出力するのか教えなくてはいけないのです。
CGI プログラムには、実行結果としてブラウザに表示する HTML 文(text/html)を出力するプログラムが多いですが、例えばアクセスカウンターのプログラムなどは「画像」を出力しますし、「PDF」を出力したり「エクセルの表計算ファイル」などを出力する事だってできます。とても便利な機構ですね。(^_^)
ヘッダーの終わりを表すには「\n\n」と、改行を2つ出力するだけです。
その次の print 行は、実際の出力を行なっています。この辺りはお馴染みですね。
このスクリプトを hello.cgi としてドキュメントルートに保存して、http://localhost/hello.cgi を開くと、以下のように表示されました。

Hello, world!

ちゃんと動きましたね。(^_^)


というわけで、lighttpd をインストールし、CGI の実行環境が整いました。どんどん CGI を勉強したいと思います!

Shebang 行について

ウェブ上で配布されている Perl で書かれた CGI プログラムを見ると、1行目には必ず

#!/usr/local/bin/perl

のような行が書かれています。この行は Shebang 行(シーバン行)と呼ばれます。


この Shebang 行は、そもそも UNIX にて「スクリプト」と「プログラム」を関連付ける為に使われていました。UNIX でファイルを実行プログラムのように実行すると、自動的に1行目が読み込まれ、スクリプトを実行するプログラムで開かれるようになっているのです。

#!/usr/local/bin/perl

の "/usr/local/bin/perl" が Perl スクリプトの実行プログラムの場所(絶対パス)を表しています。UNIX の実行プログラムには .exe のような拡張子がつかないので、このパスは「/usr/local/bin/」というディレクトリ(フォルダ)の中にある「perl」というプログラムを表している事になります。
例えば Rubyスクリプトの場合、1行目は

#!/usr/local/bin/ruby

のように Ruby スクリプトの実行プログラムを指す必要があるわけですね。(^_^)
Windows では、この関連付けの仕組みを拡張子で行なっているので、スクリプトファイルの実行に Shebang 行は必要ありません。この Shebang 行は単なるコメントとして解釈されます。


では、なぜ CGI スクリプトを書く場合にも必要なのでしょうか。世界で最も良く使われているサーバーソフトウェアとして Apache がありますが、実はこの Apache に組み込まれている CGI 機構も、スクリプトを実行するプログラムを Shebang 行から判断しているからです。
僕の場合 Windows 上で CGI プログラムの開発・テストを行うわけですが、Windows 上で Apache を実行して Perl で書いた CGI プログラムをテストする場合、1行目は以下のように書く必要があります。

#!C:/Perl/bin/perl

なぜなら Perl を実行するプログラムは「C:\Perl\bin\perl.exe」だからです。(.exe は省略可能)
実際にウェブ上で公開するには、CGI プログラムをウェブサーバーへアップロードしますが、このウェブサーバーが Apache で稼動しているなら、Shebang 行を適切なパスに直してからアップロードする必要があります。実行プログラムへのパスはサーバーによって異なりますが、「/usr/bin/perl」や「/usr/local/bin/perl」が多いようです。
これが面倒な場合は、Perl を「C:\usr\local\」など、サーバー側でのパスに合わせてインストールすればいいようです。遅かった……。(^_^;)
追記:fbis さんのコメントによると、

少々ハックっぽいですが、perl.exeそのものを「C:¥usr¥local¥」に”コピペ”すればOKだったりします。苦肉の策ということで。

との事です。なるほど! とにかく perl.exe があれば大丈夫なんですね。(^_^) インクルードパス @INC がどうなるのかちょっと心配でしたが、C:\Perl\bin\perl.exe を C:\usr\local\bin\perl.exe にコピーして試したみたところ、問題ないようです。

C:\>C:\usr\local\bin\perl.exe -e "print \"$_\n\" for (@INC)"
C:/Perl/lib
C:/Perl/site/lib
.

PATH が通っているからでしょうか? ちょっと不思議ですね。
fbis さん、便利なハック(?)、ありがとうございました!


lighttpd の場合は、Windows のように拡張子を実行プログラムに関連付けるスタイルなので、Shebang 行は使われません。ローカルでテストした後、修正せずにアップロードできるので、とても楽チンですね。
Windows でのお手軽ローカルテストには lighttpd がぴったりな気がします。


ちなみに、「Shebang 行」の由来は「#」を「She」、「!」を「Bang」と呼ぶことから来ているとか。「#」は「Sharp」ですが、それが訛ったのでしょうか。「!」はいかにも「バン!」という感じですね。(^_^)

ちょっと休憩

ぱるも日記を始めて、気が付くと 50 日を過ぎていました。
最近、家に帰るのが遅くなってしまって、日記を書いていると0時を過ぎてしまう事が多く、「昨日の日記を書いている」状態になってしまいます。良くない良くないと思いつつ、やはり今日も0時過ぎ更新。(^_^;)
大雑把ではありますが、Perl の基本的な機能はあらかた勉強できたかな、と思っています。もちろん、基礎は基礎に過ぎないので、まだまだ勉強する事は山ほどあり、どこから手をつけようか迷っています。
勉強し始める時に具体的な目標、例えば「○○が読めるようになるまでガンバル!」とか「××が作れるようになるまで!」などを決めなかったのがまずかったかも。反省。
やはり Perl と言えば CGI は避けては通れません。CGI プログラムを作ってみるのも楽しそうです。ただ、今までの簡単なプログラムと違って、実行結果が HTML になってしまうので日記には載せづらいですね……。(はてなダイアリー以外に出張スペースが必要かな?)
このまま、もうちょっと深いところまで掘り下げてみるのも面白いかも。Perl によるオブジェクト指向プログラミングなどは、まだまだ理解が浅い部分が多いです。ラクダ本も、まだ未読の部分がたくさんあります。
どうしようかな。(^_^)