작동하지 않는 Perl 스크립트가 있고 문제를 좁히는 방법을 모르겠습니다. 어떡해?
참고 : Stackoverflow에 매우 긴 답변을 추가하고 싶기 때문에 질문을 추가하고 있습니다. 나는 다른 답변에서 외부 적으로 계속 연결하고 있으며 여기에 있어야합니다. 추가 할 내용이 있으면 내 대답을 편집하는 것을 부끄러워하지 마십시오.
작동하지 않는 Perl 스크립트가 있고 문제를 좁히는 방법을 모르겠습니다. 어떡해?
참고 : Stackoverflow에 매우 긴 답변을 추가하고 싶기 때문에 질문을 추가하고 있습니다. 나는 다른 답변에서 외부 적으로 계속 연결하고 있으며 여기에 있어야합니다. 추가 할 내용이 있으면 내 대답을 편집하는 것을 부끄러워하지 마십시오.
답변:
이 답변은 Perl CGI 스크립트 문제를 해결하기위한 일반적인 프레임 워크로 의도되었으며 Perlmonks에 Perl CGI 스크립트 문제 해결 로 처음 나타났습니다 . 발생할 수있는 모든 문제에 대한 완전한 가이드도 아니며 버그 스 쿼싱에 대한 자습서도 아닙니다. 20 년 동안 CGI 스크립트를 디버깅 한 경험의 정점 일뿐입니다. 이 페이지에는 여러 집이있는 것 같고 존재한다는 사실을 잊은 것 같아서 StackOverflow에 추가하고 있습니다. bdfoy@cpan.org에서 저에게 의견이나 제안을 보낼 수 있습니다. 커뮤니티 위키이기도하지만 너무 미쳐서는 안됩니다. :)
경고를 켜면 Perl이 코드의 의심스러운 부분에 대해 경고 할 수 있습니다. -w
스위치 를 사용하여 명령 줄에서이 작업을 수행 할 수 있으므로 코드를 변경하거나 모든 파일에 pragma를 추가 할 필요가 없습니다.
% perl -w program.pl
그러나 warnings
모든 파일에 pragma를 추가하여 항상 의심스러운 코드를 정리 해야합니다.
use warnings;
짧은 경고 메시지보다 더 많은 정보가 필요한 경우 diagnostics
pragma를 사용하여 자세한 정보를 얻거나 perldiag 설명서를 참조하십시오.
use diagnostics;
서버는 CGI 스크립트의 첫 번째 출력이 CGI 헤더가 될 것으로 예상합니다. 일반적으로 그와 같은 간단 수 있습니다 print "Content-type: text/plain\n\n";
이나와 CGI.pm 및 그 유도체 print header()
. 일부 서버는 STDERR
표준 출력 (on STDOUT
) 전에 나타나는 오류 출력 (on ) 에 민감합니다 .
이 줄 추가
use CGI::Carp 'fatalsToBrowser';
스크립트에. 또한 브라우저 창에 컴파일 오류를 보냅니다. 추가 정보는 보안 위험이 될 수 있으므로 프로덕션 환경으로 이동하기 전에 제거해야합니다.
서버는 오류 로그를 보관합니다 (또는 적어도 그래야합니다). 서버와 스크립트의 오류 출력이 거기에 표시되어야합니다. 오류 로그를 찾아 내용을 확인하십시오. 로그 파일에 대한 표준 위치가 없습니다. 서버 구성에서 해당 위치를 찾거나 서버 관리자에게 문의하십시오. CGI :: Carp 와 같은 도구를 사용 하여 자신의 로그 파일을 유지할 수도 있습니다.
"Permission denied"또는 "Method not implementation"과 같은 오류가 표시되면 웹 서버 사용자가 스크립트를 읽고 실행할 수 없음을 의미합니다. Unix 버전에서는 모드를 755로 변경하는 것이 좋습니다
chmod 755 filename
.. 모드를 777로 설정하지 마십시오!
use strict
있습니까?Perl은 처음 사용할 때 자동으로 변수를 생성합니다. 이것은 기능이지만 때때로 변수 이름을 잘못 입력하면 버그가 발생할 수 있습니다. pragma
use strict
는 이러한 종류의 오류를 찾는 데 도움이됩니다. 익숙해지기 전까지는 성가신 일이지만 잠시 후에 프로그래밍이 크게 향상되고 자유롭게 다른 실수를 할 수 있습니다.
-c
스위치 를 사용하여 컴파일 오류를 확인할 수 있습니다 . 보고 된 첫 번째 오류에 집중하십시오. 헹구고 반복하십시오. 정말 이상한 오류가 발생하면 스크립트에 올바른 줄 끝이 있는지 확인하십시오. 바이너리 모드에서 FTP를 사용하거나 CVS에서 체크 아웃하거나 줄 끝 변환을 처리하지 않는 다른 작업을하는 경우 웹 서버는 스크립트를 하나의 큰 줄로 볼 수 있습니다. ASCII 모드에서 Perl 스크립트를 전송합니다.
스크립트가 안전하지 않은 종속성에 대해 불평하는 경우 아마도 -T
스위치를 사용하여 taint 모드를 켜고 있을 것입니다 . 이것은 검사되지 않은 데이터를 쉘에 계속 전달하기 때문에 좋은 것입니다. 불만이 있다면 더 안전한 스크립트를 작성하는 데 도움이됩니다. 프로그램 외부 (예 : 환경)에서 생성 된 모든 데이터는 오염 된 것으로 간주됩니다. 같은 환경 변수 PATH
와는
LD_LIBRARY_PATH
특히 곤란하다. 이 값을 안전한 값으로 설정하거나 내가 권장하는대로 완전히 설정 해제해야합니다. 어쨌든 절대 경로를 사용해야합니다. 오염 검사가 다른 문제에 대해 불평하는 경우 데이터가 오염되지 않았는지 확인하십시오. 자세한 내용은 perlsec
매뉴얼 페이지를 참조하십시오.
스크립트가 명령 줄에서 실행할 때 예상 한 결과를 출력합니까? 헤더 출력이 먼저이고 빈 줄이 뒤 따릅니 까? 그 기억하는 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 헤더 뒤에 빈 줄이 와야합니다.
웹 서버 환경은 일반적으로 명령 줄 환경보다 훨씬 더 제한적이며 요청에 대한 추가 정보가 있습니다. 스크립트가 명령 줄에서 제대로 실행되는 경우 웹 서버 환경을 시뮬레이션 할 수 있습니다. 문제가 나타나면 환경 문제입니다.
이러한 변수 설정 해제 또는 제거
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 와 같은 패키지로 동일한 기능을 얻을 수 있습니다.
스크립트가 옳은 일을하고 있다고 생각하고 수동으로 요청을 수행 할 때 올바른 출력을 얻는다면 브라우저가 범인 일 수 있습니다. 테스트하는 동안 캐시를 지우고 캐시 크기를 0으로 설정합니다. 일부 브라우저는 정말 어리 석고 사용자가 그렇게하더라도 실제로 새 콘텐츠를 다시로드하지 않습니다. 이는 URL 경로가 동일하지만 콘텐츠가 변경되는 경우 (예 : 동적 이미지) 특히 일반적입니다.
스크립트에 대한 파일 시스템 경로가 반드시 스크립트에 대한 URL 경로와 직접 관련이있는 것은 아닙니다. 이를 테스트하기 위해 짧은 테스트 스크립트를 작성해야하더라도 올바른 디렉토리가 있는지 확인하십시오. 또한 올바른 파일을 수정하고 있습니까? 변경 사항이 적용되지 않으면 다른 파일을 수정하거나 잘못된 위치에 파일을 업로드하고있을 수 있습니다. (그런데, 이것은 그러한 문제의 가장 흔한 원인입니다.)
CGI.pm
중입니까, 아니면 그 파생물을 사용하고 있습니까?문제는 CGI 입력을 구문 분석과 관련이 당신처럼 광범위하게 테스트 모듈을 사용하지 않는 경우 CGI.pm
, CGI::Request
,
CGI::Simple
또는 CGI::Lite
모듈을 사용하고 삶을 얻을.
이전 CGI 파서 구현으로 인한 입력 문제를 해결하는 데 도움이 될 수 CGI.pm
있는 cgi-lib.pl
호환성 모드가 있습니다.
system
, 백틱 또는 기타 IPC 기능을 사용하여 외부 명령을 실행
하는 경우 외부 프로그램에 대한 절대 경로를 사용해야합니다. 실행중인 항목을 정확히 알고있을뿐만 아니라 일부 보안 문제도 피할 수 있습니다. 읽기 또는 쓰기를 위해 파일을 여는 경우 절대 경로를 사용하십시오. CGI 스크립트는 현재 디렉토리에 대해 사용자와 다른 아이디어를 가질 수 있습니다. 또는 명시적인 작업을 수행 chdir()
하여 올바른 위치에 배치 할 수 있습니다 .
대부분의 Perl 함수는 작동 여부를 알려주고 $!
실패시 설정 됩니다. 반환 값을 확인하고 $!
오류 메시지 를 확인 했습니까 ? $@
사용 중인지 확인 했습니까
eval
?
Perl의 최신 안정 버전은 5.28입니다 (또는 마지막 편집시기에 따라 다릅니다). 이전 버전을 사용하고 있습니까? Perl의 버전에 따라 경고에 대한 아이디어가 다를 수 있습니다.
동일한 상황에서 다른 서버가 다르게 작동 할 수 있습니다. 동일한 서버 제품은 구성에 따라 다르게 작동 할 수 있습니다. 도움을 요청할 때 가능한 한 많은 정보를 포함하십시오.
진지한 CGI 프로그래머는 서버 기능 및 동작뿐만 아니라 로컬 구성을 포함하여 서버에 대해 가능한 한 많이 알아야합니다. 상용 제품을 사용하는 경우 서버 설명서를 사용하지 못할 수 있습니다. 그렇지 않으면 문서가 서버에 있어야합니다. 그렇지 않은 경우 웹에서 찾아보십시오.
comp.infosystems.www.authoring.cgi
?이 용도는 유용하지만 모든 좋은 포스터는 죽거나 방황했습니다.
누군가 이전에 귀하의 문제를 겪었으며 누군가 (아마도 저)가이 뉴스 그룹에서 이에 대한 답변을했을 가능성이 있습니다. 이 뉴스 그룹은 전성기를 지났지 만 과거로부터 수집 된 지혜가 때때로 유용 할 수 있습니다.
대규모 시스템에서는 너무 많은 일이 일어나기 때문에 버그를 추적하기 어려울 수 있습니다. 가능한 가장 짧은 스크립트로 문제 동작을 재현 해보십시오. 문제를 아는 것이 대부분의 해결 방법입니다. 이것은 확실히 시간이 많이 소요될 수 있지만 아직 문제를 찾지 못했고 옵션이 부족합니다. :)
진지하게. 때때로 우리는 "지각 적 좁아짐"(터널 비전)을 개발할 정도로 문제에 휩싸 일 수 있습니다. 휴식을 취하거나, 커피를 마시거나, [Duke Nukem, Quake, Doom, Halo, COD]에서 악당을 폭파하면 문제에 다시 접근하는 데 필요한 신선한 관점을 얻을 수 있습니다.
다시 진지하게. 때때로 문제를 소리내어 설명하는 것은 우리 자신의 답으로 이어집니다. 동료들이 듣지 않기 때문에 펭귄 (봉제 장난감)과 대화하십시오. 진지한 디버깅 도구로 여기에 관심이 있다면 (지금까지 문제를 발견하지 못했다면 권장합니다) 컴퓨터 프로그래밍의 심리학 을 읽어 보는 것도 좋습니다 .
$|=1
대신 $|++
?
$|=1
대신 왜 $|++
? 그것은 실제로 차이를 만들지 않으며, 그렇더라도 $|
마술 적입니다.
use strict
는 일반적으로 항상 사용하는 것이 좋지만을 사용하는 fatalsToBrowser
경우 특히 프로덕션에서 사용하는 것이 권장되지 않을 수 있습니다 die
.
die
명령문 및 기타 치명적인 런타임 및 컴파일 시간 오류가에 인쇄됩니다 STDERR
.이 오류는 찾기 어려울 수 있으며 사이트의 다른 웹 페이지에서 보낸 메시지와 결합 될 수 있습니다. 스크립트를 디버깅하는 동안 어떻게 든 브라우저에 치명적인 오류 메시지가 표시되도록하는 것이 좋습니다.
이를 수행하는 한 가지 방법은
use CGI::Carp qw(fatalsToBrowser);
스크립트 상단에 있습니다. 이 호출 은 $SIG{__DIE__}
핸들러 ( perlvar 참조 )를 설치 하여 브라우저에 치명적인 오류를 표시하고 필요한 경우 유효한 헤더를 앞에 추가합니다. 내가 들어 본 적이없는 또 다른 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__
. Join은 목록 컨텍스트를 제공하므로 <fh>는 항목 당 한 줄씩 배열을 반환합니다. 그런 다음 조인은 다시 합칩니다 ( ''로 조인). 마지막으로 eval.
eval join(q{}, <DATA>);
왜 아무도 PERLDB_OPTS
라는 옵션을 언급하지 않았는지 궁금합니다 RemotePort
. 비록 웹에서 작동하는 예제 RemotePort
가 많지는 않지만 ( perldebug 에서도 언급되지 않았습니다 )- 이 예제를 생각해내는 것은 다소 문제가 있었지만 여기에 있습니다 (리눅스 예제입니다).
적절한 예제를 수행하려면 먼저 단일 명령 줄을 통해 CGI 웹 서버의 매우 간단한 시뮬레이션을 수행 할 수있는 것이 필요했습니다. cgis를 실행하기위한 간단한 명령 줄 웹 서버를 찾은 후 . (perlmonks.org) , 이 테스트에 적용 할 수있는 IO :: All-A Tiny Web Server 를 찾았습니다 .
여기서는 /tmp
디렉토리 에서 작업하겠습니다 . CGI 스크립트는 /tmp/test.pl
아래에 포함됩니다. 있습니다 IO::All
서버는 CGI와 같은 디렉토리에 실행 파일을 제공합니다, 그래서 chmod +x test.pl
여기에 필요합니다. 따라서 일반적인 CGI 테스트 실행을 수행하기 /tmp
위해 터미널에서 디렉토리를 변경 하고 거기에서 한 줄짜리 웹 서버를 실행합니다.
$ cd /tmp
$ perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'
webserver 명령은 터미널에서 차단되고 그렇지 않으면 웹 서버가 로컬 (127.0.0.1 또는 localhost
) 에서 시작됩니다. 나중에 웹 브라우저로 이동하여이 주소를 요청할 수 있습니다.
http://127.0.0.1:8080/test.pl
... 그리고 웹 브라우저에로드되고 표시되는 print
s를 관찰해야 test.pl
합니다.
이제를 사용하여이 스크립트를 디버깅 RemotePort
하려면 먼저 네트워크에 리스너 가 있어야하며 이를 통해 Perl 디버거와 상호 작용합니다. 명령 줄 도구를 사용할 수 있습니다 netcat
( nc
, Perl 如何 remote debug? ). 따라서 먼저 netcat
한 터미널 에서 리스너를 실행합니다. 여기서 리스너는 포트 7234 (디버그 포트가 될)에서 연결을 차단하고 대기합니다.
$ nc -l 7234
그런 perl
다음가 호출 RemotePort
되었을 때 test.pl
(CGI 모드에서도 서버를 통해) 디버그 모드에서 시작 하려고 합니다 . 이것은 리눅스에서, 다음의 "오두막 래퍼"스크립트를 사용하여 수행 할 수 있습니다 - 또한 여기에 있어야 /tmp
하고 있어야 실행 할 :
cd /tmp
cat > perldbgcall.sh <<'EOF'
#!/bin/bash
PERLDB_OPTS="RemotePort=localhost:7234" perl -d -e "do '$@'"
EOF
chmod +x perldbgcall.sh
이것은 일종의 까다로운 일입니다- 쉘 스크립트 참조 -내 shebang에서 환경 변수를 어떻게 사용할 수 있습니까? -Unix 및 Linux Stack Exchange . 그러나, 여기에 트릭이 될 것으로 보인다 되지 포크에 perl
핸들 통역 test.pl
- 우리가 그것을 명중 그래서 일단, 우리가하지를 exec
, 대신 우리는 전화 perl
"분명히", 기본적으로 "소스"우리의 test.pl
스크립트를 사용하여 do
참조 ( 나는를 실행하려면 어떻게 Perl 스크립트 내에서 Perl 스크립트? ).
지금 우리가 가지고 perldbgcall.sh
에 /tmp
- 우리가 바꿀 수 test.pl
는 (대신 일반적인 펄 인터프리터)의 오두막 라인이 실행 파일을 참조 그래서, 파일 - 여기에 /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
및 새 shebang 처리기, perldbgcall.sh
에 있습니다 /tmp
. 그리고 우리는 nc
포트 7234에서 디버그 연결을 수신하고 있습니다. 그래서 마지막으로 다른 터미널 창을 열고 디렉토리를로 변경 /tmp
한 다음 1 줄 웹 서버 (포트 8080에서 웹 연결을 수신함)를 실행할 수 있습니다.
cd /tmp
perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'
이 작업이 완료되면 웹 브라우저로 이동하여 동일한 주소 인 http://127.0.0.1:8080/test.pl
. 그러나 이제 웹 서버가 스크립트를 실행하려고하면 원격 디버거 모드에서 perldbgcall.sh
시작되는 shebang을 통해 perl
실행됩니다. 따라서 스크립트 실행이 일시 중지되고 웹 브라우저가 잠기고 데이터를 기다립니다. 이제 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) 할 수 있습니다 . 스크립트가 중단 점 문을 실행합니다 (참조 : In 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
물론 이러한 종류의 디버그는 웹 서버를 실행하지 않고도 수행 할 수 있습니다. 그러나 여기서 깔끔한 것은 웹 서버를 전혀 건드리지 않는다는 것입니다. 웹 브라우저에서 "기본적으로"(CGI의 경우) 실행을 트리거합니다. CGI 스크립트 자체에서 필요한 유일한 변경 사항은 shebang의 변경입니다 (물론 shebang 래퍼 스크립트의 존재는 동일한 파일에서 실행 파일로 예배 규칙서).
글쎄, 이것이 누군가에게 도움이되기를 바랍니다-나는 이것을 직접 쓰는 대신에 우연히 발견했을 것입니다. :)
건배!
또한 Perl은 명령 줄에서 Perl 스크립트를 실행할 때 오류가 발생하는 줄을 항상 알려줄 것입니다. (예 : SSH 세션)
다른 모든 것이 실패하면 일반적으로이 작업을 수행합니다. 서버에 SSH로 접속하고 Perl 스크립트를 수동으로 실행합니다. 예를 들면 :
% perl myscript.cgi
문제가 있으면 Perl이 이에 대해 알려줄 것입니다. 이 디버깅 방법은 파일 권한 관련 문제 나 웹 브라우저 또는 웹 서버 문제를 제거합니다.
아래 명령을 사용하여 터미널에서 perl cgi-script를 실행할 수 있습니다.
$ perl filename.cgi
코드를 해석하고 결과를 HTML 코드로 제공하며 오류가있는 경우보고합니다.
perl -c filename
은 실제로 구문 만 확인합니다. 그러나 perl filename
HTML 출력을 인쇄합니다. 500 CGI 오류가 없다는 보장은 없지만 좋은 첫 번째 테스트입니다.