CGI講座 ≫ Perl ≫ 引数の受け取り方
引数受け取り
引数を渡す方法は「引数の渡し方」で説明しました。
では次にその値を受け取る方法について説明します。
まず、引数を渡す時に指定された方式、GETとPOSTでは受け取る時に違いがあります。
次のようなフォームを作ってみましょう。ファイル名はform.htmlで。
<form action="form.cgi" method="get">
名前 <input type="text" name="J_Name">
Name <input type="text" name="E_Name">
<input type="submit" value="送信">
<input type="reset" value="クリア">
</form>
GETとPOSTでそれぞれ受け取ってみます。
GETの引数受け取り
GETでは環境変数QUERY_STRINGに値がそのまま入ります。
POSTの引数受け取り
では、POSTは?
POSTは標準入力に値が入ってくるので、read(STDIN, $in, $len)関数を使って取り出します。
CGIプログラムを書く
両方ともとった値をまず&で分けて引数毎に分割し、その各引数について更に=で引数名と引数値に分割しなければなりません。
両方に対応したCGIを作ってみます。CGIファイル名はform.cgiで。呼び出し元のHTMLファイルも全部文字コードはUTFにしておいてください。
#!/usr/local/bin/perl
if($ENV{'REQUEST_METHOD'} eq 'POST') {
read(STDIN, $vars, $ENV{'CONTENT_LENGTH'});
} else {
$vars = $ENV{'QUERY_STRING'};
}
printf "Content-type: text/html\n\n";
printf '<html><head><meta charset="utf-8"></head><body>';
foreach $data (split(/&/, $vars)) {
($key, $value) = split(/=/, $data);
$value =~ tr/+/\x20/;
$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg;
$value =~ s/\t//g;
print $key . ':' . $value . "<br>";
}
printf '</body></html>';
1;
form.htmlのmethodをpostとgetで入れ替えてテストしてみてください。

結果は次のようになります。
J_Name:てすと
E_Name:test
PATH_INFO
ライブラリを使おう!
どちらにしろ割と面倒な作業になってしまいます。
自分で専用の関数を作って使いまわすのもいいんですが、わざわざ作らなくても便利なフリーのスクリプトが出回っています。それを利用しましょう。
● cgi-lib.pl
昔からよく利用されてきた有名なスクリプトです。
こちらで配布されています。
さて、この使い方ですが、GET,POSTを分けることなく一つの関数で取ることができます。
使い方は以下の通り。
cgi-lib.plを読込み、関数ReadParseを実行します。
cgi-lib.plはプログラムファイルと同じディレクトリにおいてください。
その時にローカルなハッシュ変数名を指定すると、各引数名とその値を指定したハッシュ変数に(キー、値)として格納してくれます。
この時、ハッシュ変数は*変数名という様に参照渡しします。
#cgi-lib.plを呼びます。
require "cgi-lib.pl";
#ハッシュ変数%inに引数のセットを格納する。
&ReadParse(*in);
#各変数を参照。
my $jname = $in{'J_Name'};
my $ename = $in{'E_Name'};
● CGIモジュール
CGIモジュールが用意されているperlで使えます。
こちらもGET,POSTを分けることなく一つの関数で取ることができます。
使い方は以下の通り。
CGIモジュールを読込み、new()関数により各引数値を設定し、そこから値を取ります。
#モジュールを呼びます。
use CGI;
#値を取り込む。
my $cgi = CGI::new();
#各変数を参照。
my $jname = $cgi->param('J_Name');
my $ename = $cgi->param('E_Name');
● CGI_Liteモジュール
CGI_Liteモジュールが用意されているperlで使えます。
こちらもGET,POSTを分けることなく一つの関数で取ることができます。
使い方は以下の通り。
CGI_Liteモジュールを読込み、各引数値を設定し、そこから値を取ります。
#モジュールを呼びます。
use CGI_Lite;
#値を取り込む。
my $cgi = new CGI_Lite ();
my %data = $cgi->parse_form_data();
#各変数を参照。
my $jname = $cgi->param('J_Name');
my $ename = $cgi->param('E_Name');
最初は簡単なものから作って試してみて下さい。
フォームのHTMLファイルを作り、そこからCGIを読んでみます。
GETの場合はフォームのHTMLファイルをつくらなくても直接URLを指定してCGIを呼び出せます。
また、requireやモジュールについては、Perl講座の「パッケージ・モジュール」を参照して下さい。

コマンドプロンプトで確認したとき、
Can't locate CGI_Lite.pm in @INC (@INC contains: ~~~) at form.cgi line XX. BEGIN failed--compilation aborted at form.cgi line XX.
なんて表示が出て動かない場合があります。
これは、そのモジュールがないよーってことなので、どこぞから取ってきて入れないといけません。
ライブラリの入れ方はPerl入門で説明します。
簡単なものならググって出てきたファイルをPerlのライブラリディレクトリに入れます。
Perlのライブラリディレクトリってのは、Perlをインストールしたのが
C:\Perl
だった場合は、だいたい
C:/Perl/lib
になります。この下にぶっこめばOK。
しかし!!そこにあるのに何かおかしい!って場合があります。
それは、シンボリックリンクで実際のPerlの場所とは違うディレクトリとしてPerlを実行している場合です。サーバへもっていったときに一々書き換えなくてもいいから、という理由でやってきましたが、これだと環境によってはライブラリが使えないようです。
(※実際コマンドプロンプトで「perl」とやるとオリジナルのほうを呼び出すので、↑のエラーは出ません。「C:/usr/local/bin/perl」とすると、この現象が確認できます。)
C:/perl/binにC:/usr/local/binでシンボリックリンクを貼っている場合、試しに1行目をオリジナルのほう
#!C:/perl/bin/perl
に変えて実行してみてください。それで動くようなら、ライブラリはあります。
ライブラリが入っているディレクトリの一覧は、@INCという変数に入ってます。その中から順番に見にいって、そのライブラリファイルを見つけています。
シンボリックリンクで動かすと、その@INCの値がおかしくなるようです。なので、手動で設定しましょう!
まずはライブラリの場所を確認します。
1行目をオリジナルのPerlで実行して
#!C:/perl/bin/perl
foreach $path (@INC) { print $path . "\n"; }
とすると値が出てきます。
C:/Perl/site/lib
C:/Perl/lib
.
の3っつだとします。全てを「;」で繋いだものをApacheの設定ファイルであるhttpd.confの中に追加します。
PERL5LIB C:/Perl/site/lib;C:/Perl/lib;.
これだけ。
それで、Apacheを再起動すると、この値が有効になるので、一行目をシンボリックシンク先の「#!/usr/local/bin/perl」にしてもきちんとライブラリの場所がわかるはずです。
複数の値をとる引数の場合
さて、上で紹介したモジュールを使えば簡単に渡ってきた引数が取れることがわかりました。
が、チェックボックスや複数セレクトなど、一つの引数名で複数の値を取る場合はどうなってしまうのでしょうか?
通常、上にあげたモジュールでは、引数を取り出す時に同じ引数名で複数値が存在する場合は、
各値を”\0”で繋げて返してくれます。
よって、取り出した値を”\0”で分割してあげればいいのです。
こんなフォームがあるとします。
<form action="form.cgi">
好きな色
<input type="checkbox" name="c" value="blue">青
<input type="checkbox" name="c" value="red">赤
<input type="checkbox" name="c" value="green">緑
<input type="checkbox" name="c" value="yellow">黄
趣味 <select name="joy" size="2" multiple>
<option value="1">音楽</option>
<option value="2">スポーツ</option>
<option value="3">読書</option>
</select>
<input type=submit>
</form>
この各引数を複数取り出すには、
require "cgi-lib.pl";
#ハッシュ変数%inに引数のセットを格納する。
&ReadParse(*in);
#各変数を複数参照。
my @color = split(/\0/, $in{'c'});
my @joy = split(/\0/, $in{'joy'});
このようにして取ります。
これはCGIモジュールを使うともっと簡単にできます。
●CGIモジュール
1種類の引数で複数ある値がある場合、CGIモジュールではリストで受け取ります。
CGIモジュールを読込み、new()関数により各引数値を設定し、そこから値を取ります。
#モジュールを呼びます。
use CGI;
#値を取り込む。
my $cgi = CGI::new();
#各変数を参照。
my @color= $cgi->param('c');
my @joy = $cgi->param('joy');
リストにあらかじめ分割された値が格納されています。
ファイルのアップロード
formタグでファイルをアップロードすることができます。
まず、ローカル環境側でアップロードするファイルを選択します。
<form enctype="multipart/form-data"
action="form.cgi" method="post">
普通のファイル
<input type="file" name="plain" accept="text/html,text/plain"><br>
画像ファイル
<input type="file" name="image" accept="image/jpeg">
<br>
<input type=submit value="アップロード">
</form>
このとき、formタグのmethod値をpostにすることを忘れずに。
ファイルを二つアップしてみましょう。
普通のテキストファイルと画像ファイルです。
ACCEPT属性でアップロード出来るファイルの種類を指定できますが、実際その種類以外のファイルがアップされてもブラウザ側でチェックはかかりません。
では、CGI側でファイルを受け取りましょう。
use CGI;
#値を取り込む。
my $cgi = CGI->new();
my $plainfilename = $cgi->param('plain');
my $imagefilename = $cgi->param('image');
my $BUFSZ = 2048;
my $tmp = "";
#普通のファイルを読み込み保存
while(read($plainfilename, my $buffer, $BUFSZ)){ $tmp .= $buffer; }
open(OUT, ">upfile.txt");
print(OUT $tmp);
close(OUT);
$tmp = "";
#画像ファイルを読み込み保存(jpgファイルの場合)
while(read($imagefilename, my $buffer, $BUFSZ)){ $tmp .= $buffer; }
open(OUT, ">image.jpg");
binmode(OUT);
print(OUT $tmp);
close(OUT);
まず、二つのファイル名を引数で取ります。
そのファイル名を指定してread関数を使って標準入力から取り出します。
取り出した内容をサーバ上のファイルに保存すれば終了です。
画像ファイルの場合はbinmode関数を使って、バイナリでファイルに出力します。
このサンプルプログラムはかなり簡単につくってあります。本来でしたらファイルの中身をチェックする、など行う必要があります。
保存する時のファイル名ですが、PC上のファイル名は2バイト文字の可能性もあります。
よってそのファイル名をそのまま利用すると大変危険なので、アクセス日付時間とかから適当にファイル名を作成したほうが良いでしょう。オリジナルのファイル名をとっておきたい場合は、別のデータとして保存しましょう。
環境変数PATH_INFOを使う
環境変数PATH_INFOを使った場合です。次のURLを実行してみます。
http://xxxx.aaa.com/cgi-bin/test.cgi/aaa
CGIでは、環境変数PATH_INFOで値をとってみます。
print $ENV{'PATH_INFO'};
結果は次のようになります。
/aaa
今はあまり使いません。