Perl CGI 再入門 : 開発環境・変数・use strict・コメント・文字列結合・ヒアドキュメント

大昔にいじっていた Perl CGI スクリプトを発掘した。当時はプログラミングの知識がないまま、色々な CGI スクリプトをコピペして適当に改造していたので、今更ながら Perl の復習をしようかと思う。

目次

開発環境

Perl CGI をコーディング、動作させる環境は以下のとおり。

VSCode 拡張機能の perl-syntax と、Apache の error_log$ tail -f で見ながらデバッグすることにし、Chrome ブラウザで実際に動かしながら動作確認することにする。

Apache の設定は以前紹介した記事のとおり。ドキュメントルート直下に CGI ファイルを置いたら、http://localhost/hoge.cgi とアクセスして動作する前提とする。

まずはとりあえず動かしつつ、変数を使ってみる

まずはとりあえず動かしてみよう。Apache の CGI が動作するディレクトリ配下に test.cgi というファイルを作り、以下のように書く。

#!/usr/bin/perl
# ↑ Shebang

# HTML として表示させるために書いておく
print "Content-type: text/html; charset=UTF-8\n\n";

# 変数宣言
$text = "ほげほげ";

# 変数を出力する
print $text;

Shebang は MacOS での指定。Linux とかだと #!/usr/local/bin/perl などだったりするかも。

CGI ファイルには実行権限を付けておく。$ chmod 777 test.cgi などとしておこう。

このようにコーディングし、ブラウザで http://localhost/test.cgi にアクセスすれば、ほげほげ というテキストが出力されるはずだ。

とりあえず $text と、ドル記号 $ を付けて変数を宣言することは確認できたと思う。コレが配列や連想配列になるとまた記号が変わってくるので、別途説明する。

use strict で構文チェックを厳格にする

Perl スクリプトの先頭に use strict; と書くと、厳格な文法チェックが有効になる。コレを入れると、変数宣言を強制し、変数のスコープが厳格になる。

例えば以下のようなコードは、use strict; なしだと正常に動く。

#!/usr/bin/perl

# eval-if は try-catch 的なモノ。ディレクトリを指定して開く処理
eval {
  opendir($dir, "./") or die "Can't open the directory.";
};
if($@) {
  print("MyError : $@");
  exit 1;
}

# 正常時の処理 : opendir で開いたディレクトリからファイル一覧を取得する
@files = readdir($dir);
# ファイル一覧は配列で取れるので、1つずつ取り出して出力する
foreach $file (@files) {
  print $file;
}
# ディレクトリをクローズする
closedir($dir);

このようなスクリプトファイルの先頭に use strict; を追加すると、次のようなエラーが出る。

Global symbol "$dir" requires explicit package name at /Library/WebServer/CGI-Executables/test.cgi line 18.: /Library/WebServer/CGI-Executables/test.cgi
Global symbol "@files" requires explicit package name at /Library/WebServer/CGI-Executables/test.cgi line 26.: /Library/WebServer/CGI-Executables/test.cgi
Global symbol "$dir" requires explicit package name at /Library/WebServer/CGI-Executables/test.cgi line 26.: /Library/WebServer/CGI-Executables/test.cgi
Global symbol "$file" requires explicit package name at /Library/WebServer/CGI-Executables/test.cgi line 27.: /Library/WebServer/CGI-Executables/test.cgi
Global symbol "@files" requires explicit package name at /Library/WebServer/CGI-Executables/test.cgi line 27.: /Library/WebServer/CGI-Executables/test.cgi
Global symbol "$file" requires explicit package name at /Library/WebServer/CGI-Executables/test.cgi line 28.: /Library/WebServer/CGI-Executables/test.cgi
Global symbol "$dir" requires explicit package name at /Library/WebServer/CGI-Executables/test.cgi line 30.: /Library/WebServer/CGI-Executables/test.cgi
Execution of /Library/WebServer/CGI-Executables/test.cgi aborted due to compilation errors.: /Library/WebServer/CGI-Executables/test.cgi
End of script output before headers: test.cgi

何やら登場する変数全てに対して Global symbol ... requires explicit package name というエラーが出ている。

Perl で明示的に変数宣言する場合は、my という予約語を使う必要があるので、次のようなコードに直すと正常に動作するようになる。

#!/usr/bin/perl

use strict;
# ↑ 指定しておく

my $dir;
eval {
  opendir($dir, "./") or die "Can't open the directory.";
};
if($@) {
  print("MyError : $@");
  exit 1;
}

# 正常時の処理
my @files = readdir($dir);
# ファイルを出力する
foreach my $file (@files) {
  print $file;
}
closedir($dir);

my @files や、foreach my $file などが分かりやすいところだが、変数のスコープでいうと my $dir; の宣言位置が重要。コレは eval {} ブロック内で内容を代入後、eval {} ブロック外にある readdirclosedir で使用するので、eval {} ブロックの外、手前で宣言だけしているというワケ。

この辺は ES2015 以降の constlet がスコープを持つようになったのと似ているかな。

同様に、use warnings; というモノもあるみたい。コチラは警告を出してくれるようだ。

コメントの書き方2つ

Perl の単一行コメントはシャープ # 記号。記号以降のテキストがコメントとして解釈されるので、コード行の行末に使っても良い。

# 年齢を示す変数です
my $age = 21;

my $name = 'おなまえ';  # ← 名前の変数です

複数行は Perl の POD 機能というモノを使って以下のように実現できる。

=pod
ココに
複数行コメントが
書ける
=cut

たまに記号文字が誤解釈されたりするので、多用はできない。

変数を挟んだ文字列結合のやり方2つ

Perl における文字列結合と、変数の組み合わせ方について。

Perl での文字列は、シングルクォートで囲むモノと、ダブルクォートで囲むモノがある。ダブルクォートの方は変数が展開される。

my $test = 180;

# シングルクォートで囲むと変数展開されない
print '身長は$testセンチです。';
# → 「身長は$testセンチです。」と表示される

# ダブルクォートで囲むと変数展開される
print "身長は$testセンチです。";
# → 「身長は180センチです。」と表示される

通常は上の2つ目の例のように書けばよいが、例えば、変数 $test の直後に繋がる文字列が英数字だった場合、変数名と誤解釈されてしまう。

print "身長は$testcm です。"
# 「$testcm」という変数を探してしまい、「180cm」とは表示されない

コレを解決するには、文字列結合を使う。

my $test = 180;
print '身長は' . $test . "cm です。";

このように、ピリオド . で繋げると、文字列と変数を結合できる。JavaScript でいう + 演算子に近い。「身長は」はシングルクォート、「cm です。」はダブルクォートで囲んだ文字列であるが、変数展開されるか否かの違いでしかないので、連結自体には影響がない。

my $test = 180;
print "身長は${test}cm です。";

もう一つのやり方は、変数展開させるためダブルクォートを使い、変数を $test の形式ではなく ${test} と書くと、うまくバインドできる。ES2015 でいうテンプレートリテラルに近い。

ヒアドキュメントを書く方法

Perl でヒアドキュメントを書く方法。

例えば以下のように書いていたモノも…

print "Content-Type: text/html; charset=UTF-8\n\n";
print "Hello <abbr title=\"Common Gateway Interface\">CGI</abbr>";

以下のように書ける。

print <<EOL;
Content-Type: text/html; charset=UTF-8

Hello <abbr title="Common Gateway Interface">CGI</abbr>
EOL

EOL 部分は好きな識別文字に変えられる。

そういえば、Content-Type: text/html; charset=UTF-8 の直後は改行を2つ、空行を開けないといけないのね〜。

変数にヒアドキュメントを入れて print する時はこんな感じ。

my $test = <<EOL;
<strong>hoge</strong>
<em>fuga</em>
EOL

print $test
# もしくは
print "$test"

割と Bash 風に書けるが、<<EOL; と、識別文字の宣言直後にセミコロン ; を付与して print() 関数を閉じる必要がある点に注意。今度また説明するが、関数呼び出しにはカッコを付けても変わらないので、print(<<EOL); と書いても動作する。だからセミコロンが必要なのだ。

以上

今回はココまで。

自分が経験のある言語と比較すると、Perl は Bash の雰囲気に近く、でも if ブロックの構文なんかは VBA にも近い感じがあるかな、と思った。配列の操作などは JavaScript にも通じるモノがあり、比較的とっつきやすい感じ。勿論、どっちが先とか、全ての源流の話をし始めると別なのだが、自分が経験してきた順番からこのような比較になっているだけ。

use strict によって変数宣言やスコープが厳格になるので、自分で書く際には間違いが少なくなる一方、世間的に流通している CGI スクリプトの多くは use strict が書かれておらず、読みづらいコードが多いなと思う。Strict モードでないことで動いているコードの書き方も多いので、コードの読み方も変わってくるし、参考にしづらい。だからといって Strict モードにしないと、自分が書くコードも分かりづらくなるので、やっぱり Strict は必須かな、と思う。