Perl CGIスクリプトをトラブルシューティングするにはどうすればよいですか?


100

動作していないPerlスクリプトがあり、問題の絞り込みを開始する方法がわかりません。私に何ができる?


注:Stackoverflowに非常に長い回答を追加したいので、質問を追加します。私は他の回答で外部にリンクし続けており、ここにいるに値します。追加するものがあれば、私の答えを編集するのをためらわないでください。


5
@Evan -私のポイントでも非CWとして、それはまだということだけであるである編集可能- 場合は、あなたが十分なカルマを持っています。CWの場合は100、それ以外の場合は2k。これで2060ができたので、CW以外の投稿を編集できるはずです。
マークグラベル

1
@Evan、魔法のポイントは、右側の列のツールチップにリストされています:stackoverflow.com/privileges
cjm

Webブラウザーにラインノイズが表示されている場合は、代わりにperlスクリプトが印刷されている可能性があります。その場合は、stackoverflow.com / questions / 2621161 /…を
Andrew Grimm

回答:


129

この回答は、Perl CGIスクリプトの問題を処理するための一般的なフレームワークとして意図されており、PerlmonksにはトラブルシューティングPerl CGIスクリプトとして最初に登場しました。これは、発生する可能性のあるすべての問題の完全なガイドではなく、バグ退治に関するチュートリアルでもありません。私のCGIスクリプトのデバッグ(20年以上)の集大成です。このページにはさまざまなホームがあり、存在することを忘れているようなので、StackOverflowに追加します。コメントや提案はbdfoy@cpan.orgまで送ってください。これはコミュニティーwikiでもありますが、行き過ぎてはいけません。:)


問題の発見に役立つPerlの組み込み機能を使用していますか?

警告をオンにすると、コードの疑わしい部分についてPerlが警告します。コマンドラインから-wスイッチを使用してこれを行うことができるため、コードを変更したり、プラグマをすべてのファイルに追加したりする必要はありません。

 % perl -w program.pl

ただし、warningsすべてのファイルにプラグマを追加して、問題のあるコードを常にクリアするように強制する必要があります。

 use warnings;

短い警告メッセージよりも多くの情報が必要な場合は、diagnosticsプラグマを使用して詳細情報を取得するか、perldiagのドキュメントを参照してください

 use diagnostics;

最初に有効なCGIヘッダーを出力しましたか?

サーバーは、CGIスクリプトからの最初の出力がCGIヘッダーであることを期待しています。通常、CGI.pmやその派生物print "Content-type: text/plain\n\n";同じくらい簡単print header()です。一部のサーバーは、STDERR標準出力(オンSTDOUT)の前に表示されるエラー出力(オン)に敏感です。

ブラウザにエラーを送信してみてください

この行を追加

 use CGI::Carp 'fatalsToBrowser';

あなたのスクリプトに。これにより、コンパイルエラーがブラウザウィンドウに送信されます。追加情報がセキュリティリスクになる可能性があるため、本番環境に移行する前に必ず削除してください。

エラーログは何と言っていましたか?

サーバーはエラーログを保持します(少なくとも、そうする必要があります)。サーバーとスクリプトからのエラー出力がそこに表示されます。エラーログを見つけて、その内容を確認します。ログファイルの標準的な場所はありません。サーバー構成で場所を確認するか、サーバー管理者に問い合わせてください。CGI :: Carpなどのツールを使用 して、独自のログファイルを保持することもできます。

スクリプトの権限は何ですか?

「アクセスが拒否されました」または「メソッドが実装されていません」などのエラーが表示された場合、おそらく、スクリプトがWebサーバーのユーザーによって読み取りおよび実行できないことを意味します。Unixの種類では、モードを755に変更することをお勧めします chmod 755 filename。モードを777に設定しないでください。

使っていuse strictますか?

最初に変数を使用すると、Perlは自動的に変数を作成することに注意してください。これは機能ですが、変数名を誤って入力するとバグが発生する場合があります。プラグマ use strictは、これらの種類のエラーを見つけるのに役立ちます。慣れるまでは面倒ですが、しばらくするとプログラミングが大幅に改善され、さまざまな間違いを犯すことがなくなります。

スクリプトはコンパイルされますか?

-c スイッチを使用して、コンパイルエラーをチェックできます。報告された最初のエラーに集中します。すすぎ、繰り返します。本当に奇妙なエラーが発生する場合は、スクリプトの行末が正しいことを確認してください。バイナリモードでFTPを実行する場合、CVSからチェックアウトする場合、または行末変換を処理しない何かを実行する場合、Webサーバーはスクリプトを1つの大きな行として認識する場合があります。PerlスクリプトをASCIIモードで転送します。

スクリプトは安全でない依存関係について不平を言っていますか?

スクリプトが安全でない依存関係について不平を言っている場合は、おそらく-Tスイッチを使用して汚染モードをオンにしています。これは、チェックされていないデータをシェルに渡し続けるので、これは良いことです。それが不平を言っているなら、それは私たちがより安全なスクリプトを書くのを助けるためにその仕事をしている。プログラムの外部(つまり、環境)からのデータはすべて汚染されていると見なされます。PATHや などの環境変数LD_LIBRARY_PATH は特に厄介です。私が推奨するように、これらを安全な値に設定するか、完全に設定解除する必要があります。とにかく絶対パスを使用する必要があります。汚染チェックで他の点について不満がある場合は、データを汚染していないことを確認してください。詳細については、perlsecの manページを参照してください。

コマンドラインから実行するとどうなりますか?

スクリプトは、コマンドラインから実行したときに期待どおりの結果を出力しますか?ヘッダーが最初に出力され、その後に空白行が続きますか? ターミナル(インタラクティブセッションなど)をSTDERR使用しているSTDOUT場合はとマージされる可能性があり、バッファリングが原因で順序が乱れて表示される可能性があることに注意してください。$|trueの値に設定して、Perlの自動フラッシュ機能をオンにします。通常$|++;、CGIプログラムに表示されることがあります。一度設定すると、すべての印刷と書き込みは、バッファリングされるのではなく、直ちに出力に送られます。ファイルハンドルごとにこれを設定する必要があります。次のselectように、デフォルトのファイルハンドルを変更するために使用します。

$|++;                            #sets $| for STDOUT
$old_handle = select( STDERR );  #change to STDERR
$|++;                            #sets $| for STDERR
select( $old_handle );           #change back to STDOUT

どちらの方法でも、最初の出力はCGIヘッダーであり、その後に空白行が続きます。

コマンドラインからCGIのような環境で実行するとどうなりますか?

Webサーバー環境は通常、コマンドライン環境よりもはるかに制限されており、リクエストに関する追加情報があります。スクリプトがコマンドラインから正常に実行される場合は、Webサーバー環境をシミュレートしてみてください。問題が発生した場合は、環境に問題があります。

これらの変数を設定解除または削除します

  • PATH
  • LD_LIBRARY_PATH
  • すべてのORACLE_*変数

これらの変数を設定する

  • REQUEST_METHOD(セットにGETHEADまたはPOST必要に応じて)
  • SERVER_PORT (通常80に設定)
  • REMOTE_USER (保護されたアクセスに関することをしている場合)

最近のバージョンCGI.pm(> 2.75)では-debug、古い(便利な)動作を取得するためにフラグが必要であるため、CGI.pmインポートに追加する必要がある場合があります。

use CGI qw(-debug)

使用していますdie()warn

これらの関数STDERRは、再定義しない限り印刷されます。また、CGIヘッダーも出力しません。次のようなパッケージで同じ機能を得ることができますCGI :: Carp

ブラウザのキャッシュをクリアした後はどうなりますか?

スクリプトが正しいことを実行していると考え、要求を手動で実行すると正しい出力が得られる場合は、ブラウザが原因である可能性があります。テスト中にキャッシュをクリアし、キャッシュサイズをゼロに設定します。一部のブラウザは本当に愚かであり、そうするように指示しても、実際には新しいコンテンツをリロードしないことに注意してください。これは、URLパスは同じであるがコンテンツが変化する場合(動的画像など)に特によく見られます。

脚本はあなたが思うところですか?

スクリプトへのファイルシステムパスは、必ずしもスクリプトへのURLパスに直接関連しているとは限りません。これをテストする短いテストスクリプトを記述する必要がある場合でも、適切なディレクトリがあることを確認してください。さらに、正しいファイルを変更していますか?変更しても効果が見られない場合は、別のファイルを変更しているか、ファイルを間違った場所にアップロードしている可能性があります。(ちなみに、これは私のトラブルの最も頻繁な原因です。)

あなたが使用しているCGI.pm、またはそれの派生?

あなたの問題は、CGIの入力を解析し、あなたのように広くテストモジュールを使用していないに関連している場合CGI.pmCGI::RequestCGI::SimpleまたはCGI::Lite、モジュールを使用して、生活に取得します。 CGI.pmには、cgi-lib.pl古いCGIパーサーの実装による入力問題の解決に役立つ互換モードがあります。

絶対パスを使用しましたか?

system、バックティック、またはその他のIPC機能を使用して外部コマンドを実行している場合は 、外部プログラムへの絶対パスを使用する必要があります。実行しているものを正確に知っているだけでなく、いくつかのセキュリティ問題も回避しています。読み取りまたは書き込み用にファイルを開く場合は、絶対パスを使用してください。CGIスクリプトは、現在のディレクトリについて、あなたとは異なる考えを持っている場合があります。または、明示的chdir()に行うことで、適切な場所に配置できます。

戻り値を確認しましたか?

ほとんどのPerl関数は、機能したかどうかを通知し$!、失敗した場合に設定します。戻り値をチェックし$!てエラーメッセージを調べましたか?使用していたか確認 $@しましたevalか?

どのバージョンのPerlを使用していますか?

Perlの最新の安定バージョンは5.28です(これが最後に編集された時期に応じて異なります)。古いバージョンを使用していますか?Perlのバージョンによって、警告の考え方が異なる場合があります。

どのWebサーバーを使用していますか?

同じ状況でも、サーバーによって動作が異なる場合があります。同じサーバー製品は、構成によって動作が異なる場合があります。ヘルプのリクエストには、この情報をできるだけ多く含めてください。

サーバーのドキュメントを確認しましたか?

真面目なCGIプログラマーは、サーバーの機能や動作だけでなく、ローカル構成も含めて、サーバーについて可能な限り多くを知っている必要があります。商用製品を使用している場合は、サーバーのドキュメントを入手できない場合があります。それ以外の場合、ドキュメントはサーバーにあるはずです。そうでない場合は、ウェブで探してください。

のアーカイブを検索しましたcomp.infosystems.www.authoring.cgiか?

この使用法は有用ですが、すべての優れたポスターがなくなったか、放浪しています。

誰かが以前に問題を抱えていて、誰か(おそらく私)がこのニュースグループで問題に回答している可能性があります。このニュースグループは全盛期を過ぎましたが、過去から集められた知恵が役立つこともあります。

短いテストスクリプトで問題を再現できますか?

大規模なシステムでは、非常に多くのことが起こっているため、バグを追跡するのが難しい場合があります。可能な限り短いスクリプトで問題の動作を再現してみてください。問題を知ることはほとんどの修正です。これは確かに時間がかかるかもしれませんが、まだ問題を発見しておらず、オプションが不足しています。:)

映画を見に行くことに決めましたか?

真剣に。時々、私たちは「知覚的狭窄」(トンネルビジョン)を引き起こす問題に巻き込まれることがあります。[Duke Nukem、Quake、Doom、Halo、COD]で一休みしたり、一杯のコーヒーを飲んだり、何人かの悪者を爆破したりすると、問題に再度取り組む必要のある新鮮な視点が得られる場合があります。

問題を発声しましたか?

真剣にもう一度。時々問題を声に出して説明すると、私たち自身の答えにつながります。ペンギン(ぬいぐるみ)に話しかけてください。同僚が聞いていないからです。深刻なデバッグツールとしてこれに興味がある場合(そして、問題がまだ見つからない場合はお勧めします)、The Psychology of Computer Programmingもお読みください


4
追加するものがあれば、私の答えを編集するのをためらわないでください。
ブライアン

CGIメタFAQへのリンクを削除したいようです。5.12.1は「安定」していると見なされますか?
Snake Plissken

1
$|=1代わりにしないのはなぜ$|++ですか?
reinierpost

なぜ$|=1代わりに$|++?それは実際には違いをもたらさず、それでも$|魔法です。
brian d foy 2011年

2
いい答えですが、これらのソリューションのいくつかは純粋にトラブルシューティングのためのものであり、本番用コードに含めるべきではないことに言及する価値があると思います。 use strict通常は常に使用fatalsToBrowserするのが適切ですが、特にを使用してdieいる場合は、本番環境での使用は推奨されない場合があります。
vol7ron 2012


7

デバッグ中にエラーハンドラーを使用していますか?

dieステートメントおよびその他の致命的な実行時エラーとコンパイル時エラーはに出力されますSTDERR。これは見つけるのが難しく、サイトの他のWebページからのメッセージと混同される場合があります。スクリプトのデバッグ中は、致命的なエラーメッセージをブラウザに表示することをお勧めします。

これを行う1つの方法は、

   use CGI::Carp qw(fatalsToBrowser);

スクリプトの上部。その呼び出しは、$SIG{__DIE__}ハンドラー(perlvarを参照)をインストールし、ブラウザーに致命的なエラーを表示し、必要に応じて有効なヘッダーを付加します。私がこれまで聞いたことがなかったもう1つのCGIデバッグトリックは、スクリプトのと機能CGI::Carpを使用evalしてコンパイル時エラーをキャッチすることでした。DATA__END__

   #!/usr/bin/perl
   eval join'', <DATA>;
   if ($@) { print "Content-type: text/plain:\n\nError in the script:\n$@\n; }
   __DATA__
   # ... actual CGI script starts here

このより詳細な手法には、CGI::Carpコンパイル時のエラーをより多くキャッチするという点で、わずかな利点があります。

更新:私はこれを使用したことがありませんがCGI::Debug、Mikael Sが示唆したように、この目的にも非常に便利で構成可能なツールのように見えます。


3
@Ether:<DATA>で始まる現在のスクリプトを読み取る魔法のファイルハンドル__END__です。結合はリストコンテキストを提供するため、<fh>は項目ごとに1行の配列を返します。次に、joinはそれを元に戻します( ''で結合します)。最後に、評価。
derobert

@Ether:Aライト線2に、より読みやすい方法は次のようになりますeval join(q{}, <DATA>);
derobert

@derobert:実際には、__ DATA__は__END__ではなく、データセクションを開始するために使用されるトークンです(これが私の混乱だったと思います)。
Ether

1
@Ether:実際、どちらもトップレベルのスクリプトで機能します(perldataマンページによると)。しかし、DATAが優先されるため、私は答えを変更しました。
derobert

@derobert:ドキュメントリンクをありがとう。__END__の下位互換性動作については知りませんでした。
Ether

7

PERLDB_OPTS呼ばれるオプションに誰も言及しなかったのでしょうかRemotePort。確かに、ウェブ上には多くの実用的な例RemotePortはありません(perldebugでさえ言及されていません)-そして、これを思いつくのはちょっと問題でしたが、ここにあります(Linuxの例です)。

適切な例を実行するには、最初に、できれば単一のコマンドラインを使用して、CGI Webサーバーの非常に単純なシミュレーションを実行できるものが必要でした。cgisを実行するためのシンプルなコマンドラインWebサーバーを見つけた後(perlmonks.org)、私はIO :: All-このテストに適用できる小さなWebサーバーを見つけました。

ここでは、/tmpディレクトリで作業します。CGIスクリプトは/tmp/test.pl以下に含まれます。IO::AllサーバーはCGIと同じディレクトリにある実行可能ファイルのみを提供するため、ここでchmod +x test.plは必須であることに注意してください。したがって、通常のCGIテストを実行するに/tmpは、ターミナルでにディレクトリを変更し、そこでワンライナーWebサーバーを実行します。

$ cd /tmp
$ perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'

webserverコマンドはターミナルでブロックし、それ以外の場合はローカルでWebサーバーを起動します(127.0.0.1以降localhost)-その後、Webブラウザーに移動して、このアドレスを要求できます。

http://127.0.0.1:8080/test.pl

...そして、Webブラウザーに読み込まprinttest.plて表示されたs を観察する必要があります。


ここで、このスクリプトをRemotePortでデバッグするには、最初にネットワーク上のリスナーが必要です。これを介して、Perlデバッガーと対話します。我々は、コマンドラインツールを使用することができますnetcatncのこぎりつまりここでは、:Perlの如何リモートデバッグを?)。したがって、最初netcatに1つのターミナルでリスナーを実行します。ポート7234(デバッグポートになります)で接続をブロックして待機します。

$ nc -l 7234

次に、(サーバーを介してCGIモードでも)が呼び出されたときに、を使用してperlデバッグモードで開始します。これは、Linuxでは、以下の「シェバングラッパー」スクリプト使用して行うことができます-ここにもであることが必要、としなければならない実行可能にします。RemotePorttest.pl/tmp

cd /tmp

cat > perldbgcall.sh <<'EOF'
#!/bin/bash
PERLDB_OPTS="RemotePort=localhost:7234" perl -d -e "do '$@'"
EOF

chmod +x perldbgcall.sh

これはちょっとトリッキーなことです- シェルスクリプトを参照してください-シバンで環境変数をどのように使用できますか?-UnixおよびLinuxスタック交換。しかし、ここでのトリックは、処理するインタプリタをフォークしないようです -一度ヒットした場合はそうしませんが、代わりに「明白に」呼び出し、基本的にはスクリプトを「ソース」して使用します(「How do I run a Perlのスクリプト内からPerlスクリプト?)。perltest.plexecperltest.pldo

今、私たちは持っていることperldbgcall.sh/tmp-私たちは変更することができtest.pl、それが(代わりに、通常のPerlインタプリタの)そのシェバングライン上でこの実行ファイルを参照するように、ファイルを-ここにされて/tmp/test.plこのように修正:

#!./perldbgcall.sh

# this is test.pl

use 5.10.1;
use warnings;
use strict;

my $b = '1';
my $a = sub { "hello $b there" };
$b = '2';
print "YEAH " . $a->() . " CMON\n";
$b = '3';
print "CMON " . &$a . " YEAH\n";

$DB::single=1;  # BREAKPOINT

$b = '4';
print "STEP " . &$a . " NOW\n";
$b = '5';
print "STEP " . &$a . " AGAIN\n";

現在、両方test.plとその新しいシバンハンドラーperldbgcall.shが入ってい/tmpます。そしてnc、ポート7234でデバッグ接続をリッスンしています-ようやく別のターミナルウィンドウを開き、ディレクトリを/tmpに変更して、そこでワンライナーWebサーバー(ポート8080でWeb接続をリッスンします)を実行できます。

cd /tmp
perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'

これが完了したら、Webブラウザーに移動して、同じアドレスを要求できますhttp://127.0.0.1:8080/test.pl。ただし、Webサーバーがスクリプトを実行しようとするperldbgcall.shperl、リモートデバッガーモードで起動するシバンを通じて実行されます。したがって、スクリプトの実行は一時停止します。そのため、Webブラウザーはロックされ、データを待機します。これでnetcatターミナルに切り替えることができ、おなじみのPerlデバッガーテキストが表示されます。ただし、次のように出力されますnc

$ nc -l 7234

Loading DB routines from perl5db.pl version 1.32
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(-e:1):   do './test.pl'
  DB<1> r
main::(./test.pl:29):   $b = '4';
  DB<1>

スニペットが示すように、ここでは基本的にnc「ターミナル」として使用しrます。つまり、「run」を入力(およびEnter)できるようになり、スクリプトはブレークポイントステートメントを実行します(Perlでは、$の違いは何ですか? DB :: single = 1 and 2?)、再び停止する前に(その時点で、ブラウザは引き続きロックされます)。

だから、今、私たちは、残りのステップスルーと言うことができtest.plて、ncターミナルを:

....
main::(./test.pl:29):   $b = '4';
  DB<1> n
main::(./test.pl:30):   print "STEP " . &$a . " NOW\n";
  DB<1> n
main::(./test.pl:31):   $b = '5';
  DB<1> n
main::(./test.pl:32):   print "STEP " . &$a . " AGAIN\n";
  DB<1> n
Debugged program terminated.  Use q to quit or R to restart,
  use o inhibit_exit to avoid stopping after program termination,
  h q, h R or h o to get additional info.
  DB<1>

...ただし、この時点でも、ブラウザはデータをロックして待機します。デバッガを終了した後でのみq

  DB<1> q
$

...ブラウザはロックを停止します-そして最後にtest.pl:の(完全な)出力を表示します:

YEAH hello 2 there CMON
CMON hello 3 there YEAH
STEP hello 4 there NOW
STEP hello 5 there AGAIN

もちろん、この種のデバッグはWebサーバーを実行していなくても実行できます。ただし、ここで適切なのは、Webサーバーにはまったく触れないことです。Webブラウザーから(ネイティブに)(CGIの場合)実行をトリガーします。CGIスクリプト自体で必要な唯一の変更は、shebangの変更(そしてもちろん、同じファイル内の実行可能ファイルとしてのshebangラッパースクリプトの存在)です。ディレクトリ)。

まあ、これが誰かの助けになることを願っています-私はそれを自分で書くのではなく、これに出会ったのが大好きだったでしょう:)


5

私はlog4perlを使用しています。とても便利で簡単です。

use Log::Log4perl qw(:easy);

Log::Log4perl->easy_init( { level   => $DEBUG, file    => ">>d:\\tokyo.log" } );

my $logger = Log::Log4perl::get_logger();

$logger->debug("your log message");

1

正直なところ、この投稿の上にあるすべての楽しいことを行うことができます。それでも、私が見つけた最も単純で最も積極的な解決策は、単に「印刷する」ことでした。

例:(通常のコード)

`$somecommand`;

それが本当にやりたいことを行っているかどうかを確認するには:(トラブルシューティング)

print "$somecommand";

1

コマンドラインからPerlスクリプトを実行すると、エラーが発生する行が常にPerlから通知されることにも言及する価値があります。(たとえば、SSHセッション)

他のすべてが失敗した場合、私は通常これを行います。サーバーにSSHで接続し、Perlスクリプトを手動で実行します。例えば:

% perl myscript.cgi 

問題がある場合は、Perlから通知されます。このデバッグ方法は、ファイルのアクセス権に関連する問題や、WebブラウザーやWebサーバーの問題を排除します。


Perlは、エラーが発生した行番号を常に通知するわけではありません。それは何かが間違っていると気づいた行番号を教えてくれます。エラーはすでに発生している可能性があります。
brian d foy 2014

0

以下のコマンドを使用して、ターミナルでperl cgi-scriptを実行できます

 $ perl filename.cgi

コードを解釈し、結果をHTMLコードで提供します。エラーがあれば報告します。


1
コマンド$ perl -c filename.cgiはコードの構文を検証し、エラーがあれば報告します。それはcgiのhtmlコードを提供しません。
D.Karthikeyan

呼び出す perl -c filenameは確かに構文のみをチェックします。ただしperl filename、HTML出力は印刷されます。ただし、500 CGIエラーが発生しないという保証はありませんが、最初のテストとしては優れています。
Nagev
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.