- 1.はじめに
- 2.Perlを使ってCGIを利用するために
- 2.1 Perlのパス指定
- 2.2 ヘッダの出力
- 2.3 もっとも簡単なCGIスクリプト
- 3.変数の利用
- 3.1 変数のデータ型
- 3.2 変数の種類
- 3.3 変数への値の代入
- 3.4 引用符
- 4.引数の渡し方(URLで指定)
- 4.1 URLで指定してパラメータを渡す
- 4.2 複数の引数の渡し方
- 5.ブラウザからの引数の渡し方
- 5.1 フォームから送信
- 5.2 METHOD="get"とMETHOD="post"
- 5.3 フォーム処理のCGI例(METHOD="get"の場合)
- 5.4 フォーム処理のCGI例(METHOD="post"の場合)
- 6.一歩進んだフォーム処理
- 6.1 METHODの判別処理
- 6.2 フォームの個数に左右されない処理
- 7.サブルーチンの利用
- 7.1 サブルーチンの定義
- 7.2 サブルーチンへの引数の渡し方
- 8.フォームを利用したCGIの作り方
- 8.1 処理の流れ
- 8.2 処理の例
CGIそのものはWWWサーバー上で実行できるプログラムであれば何でも利用できますが、一般には、スクリプト言語のPerlを利用することがほとんどです。そこで、Perlを使ったCGIスクリプトの作成方法について解説してみたいと思います。戻る
なお、最初にお断りしておきますが、ここでの解説は、CGIに必要なPerlスクリプトの書き方の説明が主体になっています。ホームページにどう設置するかについては、「First Step CGI」をご覧下さい。
まず利用するPerlのフルパスをスクリプトの先頭に記述します。Perlのパスはプロバイダによって異なることがありますので、分からない場合は問い合わせてください。一般的なUNIX系のWWWサーバは(aikisの場合も)、次のようになります。2.2 ヘッダの出力
#! /usr/local/bin/perl
PerlでCGIを利用するとき、実行した結果をブラウザにHTML文書として認識させるためのヘッダ出力をする必要があります。実際には作成するスクリプトに、次の1行を追加します。2.3 もっとも簡単なCGIスクリプト
print "Content-type: text/html\n\n";
この1行がないと、HTML文書と送信されず、サーバーエラーを起こすことがあります。ちなみに、このような指定にはGIFやJPEG画像などもあり、カウンタなどのプログラムでは画像を送る前に、それぞれのデータ形式にあったヘッダ情報をブラウザに対して送っているのです。
ブラウザに、単に「Hellow World!」と表示するだけのサンプルです。1行目でPerlのパスを、2行目でヘッダ情報を出力したあと、3行目以降でHTML文書のソースを出力しているだけです。戻る
【ファイル名:test1.cgi】#! /usr/local/bin/perl print "Content-type: text/html\n\n"; # HTML出力のおまじない print "\n\n"; # ¥nは改行コード print " CGI-TEST \n"; print "\n"; print "Hellow World!\n"; print "\n\n";
さて、上のtest1.cgiのように、同じ内容を表示するだけなら、わざわざCGIを使う必要などなく、直接HTML文書でその内容を表示すれよいはずです。CGIを使う以上、条件に応じて処理をしたいもので、Perlでも、変数を使って処理をすることができます。3.1 変数のデータ型
Perlで変数を用いるにあたって、使用できるデータ型は、主に3つあります。3.2 変数の種類
いずれも厳密なデータ型の区別がなく、その点ではC言語などと比べると非常に気楽にプログラミングできますが、その分プログラマが十分に把握しておく必要があります。
- 文字列
- 数値
- 配列
Perlでの変数は、適当な変数名に $, @, % を前につけることで利用できます。3.3 変数への値の代入
 $   スカラ変数   ふつうの変数   @   配列変数   要素を数字で指定   %   連想配列変数   要素を文字列で指定 
変数名は、英字または_(アンダースコア)で始まり、英数字または_で構成される単語になります。
【変数の例】
 $dasy   単なるスカラ変数   $days[11]   配列 @daysの 11番目の要素   $days{'Feb'}   連想配列の要素の一つ   $#days   配列 @daysの最後の添え字 
先ほどの変数には、全て値を代入することができます。代入は、変数を左辺値にして = で値を代入します。ある変数を別な変数に代入することもできますし、引用符で囲んだ文字列などを代入することもできます。3.4 引用符
Perlの引用符は2種類あり、それぞれ ""(ダブルコーテーション)と、''(シングルコーテーション)の2つになります。この2つのちがいは、文字列の中に含まれる変数に値を代入するか、そのまま文字列とみなすかのちがいです。 "の場合は引用符の中で変数の値を展開しますが ' の場合は変数展開を行いません。戻る
変数については、ほかにも色々と規則がありますが、詳しくはPerlのマニュアルなりを参照してください。
さて、変数が使えるだけでもプレーンなHTML文書にはないホームページを作ることができますが、次の段階としては、CGIにパラメータを渡し、そのパラメータを処理して表示していきたいものです。そこで、CGIでパラメータを処理する方法について解説します。4.1 URLで指定してパラメータを渡す
CGIにパラメータを渡す方法はいくつかありますが、まずはURLで指定する方法を紹介します。4.2 複数の引数の渡し方
http://www.hogehoge.com/~hogehoge/hogehoge.cgi?Hellow World!
このように、CGIスクリプトに ? で区切ってパラメータを続けることで、CGIスクリプトにパラメータを渡すことができます。このとき、?以下のパラメータは環境変数 QUERY_STRING に格納されます。それをCGI側で取得すればよいのです。
$buffer = $ENV{'QUERY_STRING'};
これを処理するCGIスクリプトは以下のようになります。
【ファイル名:test2.cgi】#! /usr/local/bin/perl $buffer = $ENV{'QUERY_STRING'}; # 引数を$bufferに格納する print "Content-type: text/html\n\n"; # HTML出力のおまじない print "\n\n"; # \nは改行コード print " CGI-TEST \n"; print "\n"; print "$buffer\n"; print "\n\n";
CGIスクリプトに複数のパラメータを渡したい時、MS-DOSプログラムのようにスペースで区切って渡す方法は使えません。このような場合、CGIでは複数のパラメータを一つのパラメータにまとめ、1つの引数としてわたす方法を使います。つまり、適当な区切り文字を使ってパラメータを1つにまとめ、それをスクリプト側で分解することで個々のパラメータを取得するわけです。戻る
パラメータ文字列の分解には split()関数を使用します。
次の例は、半角カンマを区切り文字にして、4つのパラメータをわたす方法です。
http://www.hogehoge.com/~hogehoge/
test3.cgi?Chuki,Computer,Circle,Language section,
【ファイル名:test3.cgi】#! /usr/local/bin/perl $buffer = $ENV{'QUERY_STRING'}; # 引数を$bufferに格納する print "Content-type: text/html\n\n"; # HTML出力のおまじない print "\n\n"; # \nは改行コード print " CGI-TEST \n"; print "\n"; ($arg1,$arg2,$arg3,$arg4) = split( /,/ , $buffer ); # ,(カンマ)で分割 print "$arg1 $arg2, $arg3, $arg4\n"; print "\n\n";
これまでの説明で、URLの中にパラメータとなる文字列を含めてCGIスクリプトに引数を渡す方法を紹介しましたが、次に、ブラウザのHTML画面からパラメータをCGIに渡す方法を紹介します。5.1 フォームから送信
HTML画面からパラメータを送る場合、フォームを使って、そこで入力されたパラメータをCGIスクリプトに渡します。5.2 METHOD="get"とMETHOD="post"
まず、次のようなフォーム入力するHTML文書があるとします。
【ファイル名:test4.html】
このHTML文書で、ブラウザ上には2つの入力フォームが表示されますが、ここに適当な文字列を入力して[送信]を押すと、以下のようなURLでCGIが呼び出されます。
http://www.hogehoge.com/~hogehoge/
test4.cgi?arg1=INPUT1&arg2=INPUT2
それぞれのフォームはフォーム名と内容が = で区切られ、さらにそれが & で区切られた形になっています。このことから、まず & を区切りに各フォームごとに分解し、さらに = で分解してパラメータを取得します。
このほか、フォームからのデータの送信には以下のような特徴がありますので、CGIスクリプト側で対応する必要があります。(この方法は後でとりあげます)
- スペースは +(プラス)に置き換えられる
- 特殊文字、日本語などは % に続く2桁の16進数に置き換えられる
フォームからCGIにデータを送信する際に、送信形式(METHOD)が2種類あり、それぞれに送信の手順が異なります。いずれの場合もフォームの内容自体は、=と&で区切られた形でCGIに渡されますが、その内容がどのように受け渡しされるかが大きく異なります。5.3 フォーム処理のCGI例(METHOD="get"の場合)
上の例は METHOD="get"を使っている例ですが、この時引数はURLで指定した時と同じように、環境変数 QUERY_STRING に格納され、それを参照することでスクリプトに取り込みます。
一方、METHOD="post"を使った場合、引数列は標準入力としてサーバーからCGIスクリプトに渡される事になります。(このためサーバーが METHOD="post"をサポートしている必要があります)
この2つには、格納形式の違いの他に以下のような特徴があります。
- 環境変数には長さに制限があるので、getの場合途中でパラメータが切れることがある
- ブラウザがIEの時、フォームに<TEXTAREA>があるときはGETは使えない
- postの場合、パラメータの長さが。環境変数 CONTENT_LENGTH に格納される
これらのことから、CGIでは通常METHOD="post"を使うことが多いのです。
6.1のHTML文書のフォームで、METHOD="get"で送られてくるパラメータを処理するCGIスクリプトは次のようになります。5.4 フォーム処理のCGI例(METHOD="post"の場合)
【ファイル名:test4.cgi】#! /usr/local/bin/perl $buffer = $ENV{'QUERY_STRING'}; # 引数を$bufferに格納する ($arg1,$arg2) = split( /&/ , $buffer ); #パラメータごとに分割 ($name1,$value1) = split( /=/ , $arg1 ); #フォーム名と内容に分割 ($name2,$value2) = split( /=/ , $arg2 ); # 〃 $value1 =~ tr/+/ /; # フォーム形式の + を、スペースに変換 $value2 =~ tr/+/ /; # 〃 # フォーム形式の特殊文字・漢字を変換する処理 $value1 =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value2 =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; print "Content-type: text/html\n\n"; # HTML出力のおまじない print "\n\n"; # \nは改行コード print " CGI-TEST \n"; print "\n"; print "第1パラメータ = $value1\n"; print "第2パラメータ = $value2\n"; print "\n\n";
6.1のHTML文書のフォームで、METHOD="post"で送られてくるパラメータを処理するCGIスクリプトは次のようになります。この場合、標準入力からパラメータを受け取る必要があるので、まず入力されるパラメータ文字列のサイズを、環境変数 CONTENT_LENGTH で取得し、そのサイズ分だけ標準入力から読みとります。戻る
【ファイル名:test4.cgi(postバージョン)】#! /usr/local/bin/perl $length = $ENV{'CONTENT_LENGTH'}; # パラメータ文字列の長さ read(STDIN, $buffer, $length ); # 長さ分だけ標準入力から読み込む ($arg1,$arg2) = split( /&/ , $buffer ); #パラメータごとに分割 ($name1,$value1) = split( /=/ , $arg1 ); #フォーム名と内容に分割 ($name2,$value2) = split( /=/ , $arg2 ); # 〃 $value1 =~ tr/+/ /; # フォーム形式の + を、スペースに変換 $value2 =~ tr/+/ /; # 〃 # フォーム形式の特殊文字・漢字を変換する処理 $value1 =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value2 =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; print "Content-type: text/html\n\n"; # HTML出力のおまじない print "\n\n"; # ¥nは改行コード print " CGI-TEST \n"; print "\n"; print "第1パラメータ = $value1\n"; print "第2パラメータ = $value2\n"; print "\n\n";
実際のCGIでは、処理する内容に応じてMETHODが違ったり、渡したいパラメータの数や内容が異なります。このようなパラメータの数が処理する内容によって異なる場合でも、フォームから送られてきた内容を処理できる方法を紹介します。6.1 METHODの判別処理
METHODがgetかpostかの判定は、環境変数 REQUEST_METHOD で判定できます。6.2 フォームの個数に左右されない処理
実際の処理は以下のようになります。
if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); } else { $buffer = $ENV{'QUERY_STRING'}; }
戻る@pairs = split(/&/,$buffer); # 各パラメータを配列に格納する foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); # フォーム名と値に分解 $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $FORM{$name} = $value; } この処理で、フォームからのパラメータは、$FORM{'フォーム名'}という連想配列に格納されます。なお、取り込んだパラメータの呼び出しは次のように行います。
print "$FORM{'arg1'}\n";
print "$FORM{'arg2'}\n";
実際にCGIスクリプトに組み込む場合は、上記の2種類の処理を1つのサブルーチンにまとめて、スクリプトの先頭部分で、そのサブルーチンを呼び出すようにすればよいでしょう。
さて、CGIスクリプトも少し長くなってくるとともに、毎回同じような処理も増えてきます。先ほどのフォーム処理などは特にそうでしょう。このような処理はサブルーチン化して呼び出す方がよいと思います。7.1 サブルーチンの定義
Perlでサブルーチンを定義するのは以下のように行います。(先ほどのフォーム処理です)7.2 サブルーチンへの引数の渡し方
sub form { if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); } else { $buffer = $ENV{'QUERY_STRING'}; } @pairs = split(/&/,$buffer); # 各パラメータを配列に格納する foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); # フォーム名と値に分解 $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $FORM{$name} = $value; } }
スクリプト内でこのサブルーチンを呼び出す時は、サブルーチン名に & をつけて呼び出します。
&form;
これを、CGIプログラムの先頭に記入するだけで$FORMの内容をつかむことが出来ます。
サブルーチンに引数を渡したいとき、呼び出し側ではごく一般的な方法で呼び出します。戻る
&hogehoge( a, b, c, d );
この時引数の各要素は、Perlの予約変数配列の @_ すなわち、$_[0], $_[1}, $_[2],,,に格納されます。したがって、上の例では次のように格納されます。
$_[0] = a;
$_[1] = b;
$_[2] = c;
$_[3] = d;
ブラウザのフォームを利用してCGI処理を行う場合、どのように処理の流れを考えて行くのか、また、その方法はどうすればよいのか、などについて紹介します。8.1 処理の流れ
例として、次のようなHTML文書を出力するCGIとその処理を考えます。(内容は自動登録ができるリンクスクリプトとでもしましょうか)このフォームから送られてくるデータの処理を考えます。
自動リンクシステム
まず、フォームを利用したCGIの特徴は、(極端な意見ではありますが)『HTML画面を出力した時点でプログラムは一度終了している。』という事ではないかと思います。8.2 処理の例
つまり、このCGIで必要な最初の処理は、『機能の選択』画面を出力する事ですが、この画面を終了した時点でスクリプトの処理も終わっている事になります。リンクを表示したり、登録・削除・編集といった各機能を処理したくても、スクリプトはすでに終了しているのです。
そこで、このような場合、次にする処理内容が何かをパラメータとしてCGIに送り直して処理をすすめる方法を使うことでこの問題を解決します。上の例では、フォーム名 task が次のスクリプトの動作を蹴ってするパラメータになっています。taskの内容に応じて処理を振り分ければよいのです。
このCGIの場合の流れを見てみます。
- CGIスクリプトが実行される
- フォームのからのパラメータの取得
- $FORM{'task'}の内容がない場合(初めて実行の時など)は『機能選択画面』へ
- $FORM{'task'}にパラメータが入力されている場合、その内容に応じて処理
少し長くなりますが、処理の例を紹介します。戻る
【ファイル名:test5.cgi】#! /usr/local/bin/perl &form; # フォームデータの取得 if ($FORM{'task'} eq "list") { &list; } # リスト表示ルーチンへ elsif ($FORM{'task'} eq "regist") { ®ist; } # 登録ルーチンへ elsif ($FORM{'task'} eq "delet") { &delet; } # 削除ルーチンへ elsif ($FORM{'task'} eq "edit") { &edit; } # 編集ルーチンへ else { &main; } # メイン画面へ #--------------------------- # フォームデータの取得 #--------------------------- sub form { if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); } else { $buffer = $ENV{'QUERY_STRING'}; } @pairs = split(/&/,$buffer); # 各パラメータを配列に格納する foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); # フォーム名と値に分解 $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $FORM{$name} = $value; } } #--------------------------- # 通常の「機能の選択画面」 #--------------------------- sub main { print "Content-type: text/html\n\n"; # HTML出力のおまじない print "\n\n"; # ¥nは改行コード print " \n"; print "\n\n"; exit; # プログラム終了 } #--------------------------- # リンクの表示 #--------------------------- sub list { # # 処理を記述 # exit; # プログラム終了 } #--------------------------- # リンクの登録 #--------------------------- sub regist { # # 処理を記述 # exit; # プログラム終了 } #--------------------------- # リンクの削除 #--------------------------- sub delet { # # 処理を記述 # exit; # プログラム終了 } #--------------------------- # リンクの編集 #--------------------------- sub edit { # # 処理を記述 # exit; # プログラム終了 }自動リンクシステム \n"; print "\n\n"; print "