動作していないPerlスクリプトがあり、問題の絞り込みを開始する方法がわかりません。私に何ができる?
注:Stackoverflowに非常に長い回答を追加したいので、質問を追加します。私は他の回答で外部にリンクし続けており、ここにいるに値します。追加するものがあれば、私の答えを編集するのをためらわないでください。
動作していないPerlスクリプトがあり、問題の絞り込みを開始する方法がわかりません。私に何ができる?
注:Stackoverflowに非常に長い回答を追加したいので、質問を追加します。私は他の回答で外部にリンクし続けており、ここにいるに値します。追加するものがあれば、私の答えを編集するのをためらわないでください。
回答:
この回答は、Perl CGIスクリプトの問題を処理するための一般的なフレームワークとして意図されており、PerlmonksにはトラブルシューティングPerl CGIスクリプトとして最初に登場しました。これは、発生する可能性のあるすべての問題の完全なガイドではなく、バグ退治に関するチュートリアルでもありません。私のCGIスクリプトのデバッグ(20年以上)の集大成です。このページにはさまざまなホームがあり、存在することを忘れているようなので、StackOverflowに追加します。コメントや提案はbdfoy@cpan.orgまで送ってください。これはコミュニティーwikiでもありますが、行き過ぎてはいけません。:)
警告をオンにすると、コードの疑わしい部分についてPerlが警告します。コマンドラインから-w
スイッチを使用してこれを行うことができるため、コードを変更したり、プラグマをすべてのファイルに追加したりする必要はありません。
% perl -w program.pl
ただし、warnings
すべてのファイルにプラグマを追加して、問題のあるコードを常にクリアするように強制する必要があります。
use warnings;
短い警告メッセージよりも多くの情報が必要な場合は、diagnostics
プラグマを使用して詳細情報を取得するか、perldiagのドキュメントを参照してください。
use diagnostics;
サーバーは、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ヘッダーであり、その後に空白行が続きます。
Webサーバー環境は通常、コマンドライン環境よりもはるかに制限されており、リクエストに関する追加情報があります。スクリプトがコマンドラインから正常に実行される場合は、Webサーバー環境をシミュレートしてみてください。問題が発生した場合は、環境に問題があります。
これらの変数を設定解除または削除します
PATH
LD_LIBRARY_PATH
ORACLE_*
変数これらの変数を設定する
REQUEST_METHOD
(セットにGET
、HEAD
または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.pm
、CGI::Request
、
CGI::Simple
またはCGI::Lite
、モジュールを使用して、生活に取得します。
CGI.pm
には、cgi-lib.pl
古いCGIパーサーの実装による入力問題の解決に役立つ互換モードがあります。
system
、バックティック、またはその他のIPC機能を使用して外部コマンドを実行している場合は
、外部プログラムへの絶対パスを使用する必要があります。実行しているものを正確に知っているだけでなく、いくつかのセキュリティ問題も回避しています。読み取りまたは書き込み用にファイルを開く場合は、絶対パスを使用してください。CGIスクリプトは、現在のディレクトリについて、あなたとは異なる考えを持っている場合があります。または、明示的chdir()
に行うことで、適切な場所に配置できます。
ほとんどのPerl関数は、機能したかどうかを通知し$!
、失敗した場合に設定します。戻り値をチェックし$!
てエラーメッセージを調べましたか?使用していたか確認
$@
しましたeval
か?
Perlの最新の安定バージョンは5.28です(これが最後に編集された時期に応じて異なります)。古いバージョンを使用していますか?Perlのバージョンによって、警告の考え方が異なる場合があります。
同じ状況でも、サーバーによって動作が異なる場合があります。同じサーバー製品は、構成によって動作が異なる場合があります。ヘルプのリクエストには、この情報をできるだけ多く含めてください。
真面目なCGIプログラマーは、サーバーの機能や動作だけでなく、ローカル構成も含めて、サーバーについて可能な限り多くを知っている必要があります。商用製品を使用している場合は、サーバーのドキュメントを入手できない場合があります。それ以外の場合、ドキュメントはサーバーにあるはずです。そうでない場合は、ウェブで探してください。
comp.infosystems.www.authoring.cgi
か?この使用法は有用ですが、すべての優れたポスターがなくなったか、放浪しています。
誰かが以前に問題を抱えていて、誰か(おそらく私)がこのニュースグループで問題に回答している可能性があります。このニュースグループは全盛期を過ぎましたが、過去から集められた知恵が役立つこともあります。
大規模なシステムでは、非常に多くのことが起こっているため、バグを追跡するのが難しい場合があります。可能な限り短いスクリプトで問題の動作を再現してみてください。問題を知ることはほとんどの修正です。これは確かに時間がかかるかもしれませんが、まだ問題を発見しておらず、オプションが不足しています。:)
真剣に。時々、私たちは「知覚的狭窄」(トンネルビジョン)を引き起こす問題に巻き込まれることがあります。[Duke Nukem、Quake、Doom、Halo、COD]で一休みしたり、一杯のコーヒーを飲んだり、何人かの悪者を爆破したりすると、問題に再度取り組む必要のある新鮮な視点が得られる場合があります。
真剣にもう一度。時々問題を声に出して説明すると、私たち自身の答えにつながります。ペンギン(ぬいぐるみ)に話しかけてください。同僚が聞いていないからです。深刻なデバッグツールとしてこれに興味がある場合(そして、問題がまだ見つからない場合はお勧めします)、The Psychology of Computer Programmingもお読みください。
$|=1
代わりにしないのはなぜ$|++
ですか?
$|=1
代わりに$|++
?それは実際には違いをもたらさず、それでも$|
魔法です。
use strict
通常は常に使用fatalsToBrowser
するのが適切ですが、特にを使用してdie
いる場合は、本番環境での使用は推奨されない場合があります。
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が示唆したように、この目的にも非常に便利で構成可能なツールのように見えます。
<DATA>
で始まる現在のスクリプトを読み取る魔法のファイルハンドル__END__
です。結合はリストコンテキストを提供するため、<fh>は項目ごとに1行の配列を返します。次に、joinはそれを元に戻します( ''で結合します)。最後に、評価。
eval join(q{}, <DATA>);
と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ブラウザーに読み込まprint
れtest.pl
て表示されたs を観察する必要があります。
ここで、このスクリプトをRemotePort
でデバッグするには、最初にネットワーク上のリスナーが必要です。これを介して、Perlデバッガーと対話します。我々は、コマンドラインツールを使用することができますnetcat
(nc
のこぎりつまりここでは、:Perlの如何リモートデバッグを?)。したがって、最初netcat
に1つのターミナルでリスナーを実行します。ポート7234(デバッグポートになります)で接続をブロックして待機します。
$ nc -l 7234
次に、(サーバーを介してCGIモードでも)が呼び出されたときに、を使用してperl
デバッグモードで開始します。これは、Linuxでは、以下の「シェバングラッパー」スクリプト使用して行うことができます-ここにもであることが必要、としなければならない実行可能にします。RemotePort
test.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スクリプト?)。perl
test.pl
exec
perl
test.pl
do
今、私たちは持っていること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.sh
とperl
、リモートデバッガーモードで起動するシバンを通じて実行されます。したがって、スクリプトの実行は一時停止します。そのため、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ラッパースクリプトの存在)です。ディレクトリ)。
まあ、これが誰かの助けになることを願っています-私はそれを自分で書くのではなく、これに出会ったのが大好きだったでしょう:)
!
コマンドラインからPerlスクリプトを実行すると、エラーが発生する行が常にPerlから通知されることにも言及する価値があります。(たとえば、SSHセッション)
他のすべてが失敗した場合、私は通常これを行います。サーバーにSSHで接続し、Perlスクリプトを手動で実行します。例えば:
% perl myscript.cgi
問題がある場合は、Perlから通知されます。このデバッグ方法は、ファイルのアクセス権に関連する問題や、WebブラウザーやWebサーバーの問題を排除します。
以下のコマンドを使用して、ターミナルでperl cgi-scriptを実行できます
$ perl filename.cgi
コードを解釈し、結果をHTMLコードで提供します。エラーがあれば報告します。
perl -c filename
は確かに構文のみをチェックします。ただしperl filename
、HTML出力は印刷されます。ただし、500 CGIエラーが発生しないという保証はありませんが、最初のテストとしては優れています。