デバッグ

■ 文法ミスをチェックする(-wc)

◆ -wc オプション

perl を -wc オプションで実行することにより、スクリプトファイルの文法チェックを行うことができます。-w は警告(ワーニング)メッセージまで表示することを、-c はスクリプトの構文チェックのみを行って実行せずに終了することを意味します。-w により、通常はエラーにはならない、警告レベルの指摘もチェックすることができます。

◆ チェックの実例

例えば、下記のようなスクリプトがあるとします。

$num = 26;
$msg = num;
print "$msg\n";

$msg = $num; とするつもりが、$ を書き忘れて $msg = num; となっています。Perl では、無理にダブルクォーテーションで囲まなくても、それが文字列としてしか解釈できないものであれば文字列として解釈してしまうので、26 と表示して欲しいところ、次のように実行されてしまいます。

% perl test.pl
num
%

これを perl の -wc オプションでチェックしてみましょう。

% perl -wc test.pl
Unquoted string "num" may clash with future reserved word at test.pl
line 2.
Name "main::num" used only once: possible typo at test.pl line 1.
test.pl syntax OK
%

test.pl の 2行目で "..." で囲まれていない num がある旨と、そのため、1行目で現れる main::num という変数が設定だけされて、一度も参照されていない旨のワーニングメッセージが表示されます。Perl のプログラミングが完了したら、念のため、-wc オプションで必ずチェックするという習慣を身につけましょう。

■ CGIを大胆に文法チェックする

◆ 大胆デバッグ

TELNET でログインできないプロバイダなどでは、perl -wc オプションで文法チェックを行うことができません。そんな時は、スクリプト全体を eval { ... } で囲むことにより、Perl の文法ミスをブラウザに表示することが可能になります。

◆ 大胆デバッグの例

例えば、下記のような CGI がうまく動作しないとします。

#!/usr/local/bin/perl

require "jcode.pl";
print "Content-type: text/html\n\n";
print "<html>\n";
print "<head><title>TEST</title></head><body>TEST</body>\n";
print "</html>\n";

これに、下記のチェックルーチンを追記します。

#!/usr/local/bin/perl
eval {
require "jcode.pl";
print "Content-type: text/html\n\n";
print "<html>\n";
print "<head><title>TEST</title></head><body>TEST</body>\n";
print "</html>\n";
};
if ($@) {        # $@ は eval 内で発生したエラーメッセージを示します。
   print "Content-type: text/html\n\n";
   print "<title>TEST-DEBUG</title><hr>$@<hr>\n";
}

これをブラウザから CGI として呼び出すと、例えば次のようなメッセージが表示されます。

Can't locate jcode.pl in @INC (@INC contains: C:/Perl/lib
C:/Perl/site/lib .) at C:\HomePage\xx.cgi line 5.

このメッセージから、jcode.pl が C:/Perl/lib や C:/Perl/site/lib やカレントフォルダから見付からないのがエラーの原因であることが推測できます。

■ ワーニングメッセージを出力する(warn)

◆ 説明

warn() は、標準エラー出力(STDERR)にワーニング(警告)メッセージを出力します。動作は「print STDERR メッセージ」と似ていますが、die() と同様、メッセージの最後に改行が無い場合、スクリプトファイル名と行番号がメッセージに付加されます。

if (open(IN, "file.txt")) {
    print <IN>;
    close(IN);
} else {
    warn "Can't open file.txt";
}

標準エラー出力は、CGI の結果としては取得できませんので、上記は、CGI のデバッグには不向きです。

■ ファイル名と行番号付きでデバッグ文を表示する(__FILE__, __LINE__)

◆ 説明

特殊変数 __FILE__ および __LINE__ は、現在実行しているスクリプトのファイル名と行番号を示します。これをスクリプトの適当な位置に記述して実行することにより、スクリプトのどの部分が実行されているかトレースすることができるようになります。

printf("DEBUG: %s(%d)\n", __FILE__, __LINE__);

CGI としてデバッグする際には、必ず、最初のデバッグ文書き出しよりも前に、Content-type のヘッダを書き出すようにしてください。

■ 標準エラー出力をファイルに書き込む

◆ 通常のリダイレクト

> によるファイルへの書き込み(リダイレクト)は通常、標準出力の結果のみをファイルに書き出し、標準エラー出力の結果はコンソールに書き出します。

print STDOUT "This is STDOUT message.\n";
print STDERR "This is STDERR message.\n";

これを下記のようにして起動すると、コンソールには「This is STDERR message.」が表示され、ファイルには「This is STDOUT message.」が記録されます。

% perl test.pl > test.log
This is STDERR message.
◆ 標準エラー出力をリダイレクトする(UNIX-csh系)

UNIX の csh 系のシェルを使用している場合は、次のようにすることで、標準エラー出力もファイルに記録することができます。

% perl test.pl >& test.log
◆ 標準エラー出力をリダイレクトする(UNIX-bsh系 / Windows系)

UNIX の bsh 系のシェルや、最近の Windows の DOSプロンプトを使用している場合は、次のようにすることで、標準エラー出力もファイルに記録することができます。

% perl test.pl > test.log 2>&1
◆ 標準エラー出力のみをリダイレクトする

標準出力と標準エラー出力を別々のファイルに保存したい場合は次のようにします。まずは csh 系のシェルの場合。

% (perl test.pl > stdout.log) >& stderr.log

bsh 系や Windows の場合は次のようになります。

% (perl test.pl > stdout.log) > stderr.log 2>&1

■ 堅実なコードを書く(strict)

◆ strictによる堅実コード

use strict を宣言することにより、不注意によるバグの少ない、堅実なコードを書くことができるようになります。

use strict;

my($max);
$max = 5;

Test();

sub Test {
    my($i);
    for ($i = 0; $i > $max; $i++) {
        print "$i\n";
    }
}

use strict; の部分は、以下のように記述することもできます。

use strict;
use strict 'vars';
use strict 'refs';
use strict 'subs';

vars は、ローカル変数は必ず my() で宣言する、グローバル変数は必ず $Test1::xx のようにパッケージ名で修飾することをプログラマに強制します。この規則を守らないとプログラムが強制終了します。

refs は、シンボリックなリファレンス(詳細省略)を用いた際にエラーが発生するにようにします。

subs は、$SIG{TERM} = "func"; などの関数指定の祭に、"func" を func と記述してしまうことを抑止します。

use strict; は、'vars'、'refs'、'subs' を全て指定したことになります。

■ perl をデバッグモードで起動する

◆ 説明

-d オプションにより、perl を デバッグモード で起動することができます。デバッグモードでは、perl を 1行ずつ実行しながら、その都度変数の内容をチェックすることができます。

◆ コマンド実行例
perl -d test.pl
    :
main::(test.pl:1)  $a = 5;
  DB<1> s                          ← 1行目を実行
main::(test.pl:2): $b = $a * 5;
  DB<1>                            ← 直前の s か n を繰り返し
main::(test.pl:4): print "$b\n";
  DB<1> p $b                       ← $b の値を表示
25
  DB<1> q                          ← 終了
◆ 主なデバッグコマンド

デバッグコマンドのほんの一例を示します。この他にもいろいろなコマンドや機能がサポートされています。詳細は、コマンドプロンプト から perldoc perldebug を実行してください。

コマンド説明
lスクリプトの数行を表示。(list)
s1行実行。関数の場合は関数の中に入る。(single step)
n1行実行。関数の場合は関数をまとめて実行する。(next)
p 変数名変数の値を表示。(print)
q終了。(quit)
b 行番号指定行にブレークポイントを設定。(breakpoint)
c次のブレークポイントまで実行。(continue)

■ 変数の参照時、設定時の動作を制御する

◆ 変数とモジュールの関連付け

下記のようなモジュールを用意し、tie() によって変数とモジュールを関係付けることにより、変数に値が設定されたときなどにモジュール内のサブルーチンを呼び出すことができます。untie() は関連付けを終了します。配列、連想配列などに場合は、呼び出されるメソッドが異なりますので、詳細は Perl のリファレンスを参照してください。

ValWatch.pm
package ValWatch;

sub TIESCALAR {    # 変数がtieされたとき
    my($pkg, $name) = @_;
    my($obj) = { name => $name, value => "" };
    bless($obj, $pkg);
    print "$obj->{name} が tie されました。\n";
    return($obj);
}
sub STORE {       # 変数に設定されたとき
    my($obj, $value) = @_;
    print "$obj->{name} に $value が代入されました。\n";
    $obj->{value} = $value;
    return($obj->{value});
}
sub DESTROY {     # 変数が消滅するとき
    my($obj) = @_;
}
1;

次のように使用します。

use ValWatch;
tie($xx, "ValWatch", "xx");
$xx = 222; $xx = 333;
untie($xx);

実行例は次のようになります。

xx が tie されました。
xx に 222 が代入されました。
xx に 333 が代入されました。

Copyright (C) 2002 杜甫々