У меня есть Perl script, который не работает, и я не знаю, как начать сужение проблемы. Что я могу сделать?
Примечание. Я добавляю вопрос, потому что я действительно хочу добавить свой очень длинный ответ в Stackoverflow. В других ответах я держу внешние ссылки, и он заслуживает того, чтобы быть здесь. Не стесняйтесь редактировать мой ответ, если вам есть что добавить.
Этот ответ предназначен в качестве общей основы для проблемы с сценариями Perl CGI и первоначально появились на Perlmonks как Устранение неполадок Perl CGI Scripts. Это не полное руководство для каждого проблема, с которой вы можете столкнуться, или учебник по раздаче ошибок. Это является лишь кульминацией моего опыта отладки сценариев CGI за десять (плюс!) года. На этой странице было много разных домов, и мне кажется чтобы забыть его, поэтому я добавляю его в StackOverflow. Вы может отправлять любые комментарии или предложения мне по адресу [email protected]. Это также сообщество wiki, но не стоит слишком гадко.:)
Включите предупреждения, чтобы Perl предупредил вас о сомнительных частях вашего кода. Это можно сделать из командной строки с помощью переключателя -w
, поэтому вам не нужно изменять какой-либо код или добавлять прагму в каждый файл:
% perl -w program.pl
Однако вы должны заставить себя всегда прояснять сомнительный код, добавляя прагму warnings
ко всем вашим файлам:
use warnings;
Если вам нужна дополнительная информация, чем короткое предупреждающее сообщение, используйте прагму diagnostics
, чтобы получить дополнительную информацию, или посмотрите perldiag документация:
use diagnostics;
Сервер ожидает первого выхода из CGI
script - заголовок CGI. Обычно это может быть
просто как print "Content-type: text/plain\n\n";
или CGI.pm и его производные, print
header()
. Некоторые серверы чувствительны к выходу ошибок
(на STDERR
) до стандартного вывода (на STDOUT
).
Добавьте строку
use CGI::Carp 'fatalsToBrowser';
на ваш script. Это также отправляет ошибки компиляции в окно браузера. Обязательно удалите это, прежде чем переходить в производственную среду, поскольку дополнительная информация может представлять угрозу безопасности.
Серверы сохраняют журналы ошибок (или они должны, по крайней мере). Ошибка вывода с сервера и вашего script должна там появляются. Найдите журнал ошибок и посмотрите, что он говорит. Не существует стандартного места для файлов журналов. Посмотрите в конфигурации сервера для их местоположения или спросить сервер админ. Вы также можете использовать такие инструменты, как CGI::Carp чтобы сохранить ваши собственные файлы журналов.
Если вы видите ошибки, такие как "Разрешение отказа", или "Метод не
реализован ", это, вероятно, означает, что ваш script не является
читаемый и исполняемый пользователем веб-сервера. О вкусах
Unix, рекомендуется изменить режим на 755:
chmod 755 filename
. Никогда не устанавливайте режим на 777!
use strict
?Помните, что Perl автоматически создает переменные, когда
вы сначала используете их. Это функция, но иногда она может
вызывают ошибки, если вы ошибочно называете имя переменной. Прагма
use strict
поможет вам найти такие виды
ошибки. Это раздражает, пока вы не привыкнете к нему, но
программирование значительно улучшится через некоторое время и
вы сможете свободно совершать разные ошибки.
Вы можете проверить ошибки компиляции с помощью -c
переключатель. Сосредоточьтесь на первых сообщенных ошибках. Полоскание,
повторение. Если вы получаете действительно странные ошибки, проверьте
убедитесь, что ваш script имеет правильные окончания строки. если ты
FTP в двоичном режиме, проверка из CVS или что-то еще, что
не обрабатывает перевод строки, веб-сервер может видеть
ваш script как одна большая строка. Перенос Perl-скриптов в ASCII
Режим.
Если ваш script жалуется на небезопасные зависимости, вы
вероятно, используют переключатель -T
, чтобы включить режим taint, который
хорошая вещь, поскольку она позволяет вам передавать неконтролируемые данные в оболочку. Если
он жалуется, что делает свою работу, чтобы помочь нам написать более безопасные скрипты. Любые
данные, происходящие вне программы (т.е. окружающая среда)
считается испорченным. Переменные среды, такие как PATH
и
LD_LIBRARY_PATH
особенно хлопотно. Вы должны установить их в безопасное значение
или полностью отключить их, как я рекомендую. Вы должны использовать абсолютный
пути в любом случае. Если проверка жалости жалуется на что-то еще,
убедитесь, что вы не очистили данные. См. perlsec
man page для деталей.
Выводит ли script то, что вы ожидаете при запуске с
командная строка? Сначала вывод заголовка, за которым следует
пустая строка? Помните, что STDERR
можно объединить с STDOUT
если вы находитесь на терминале (например, в интерактивном сеансе) и
из-за буферизации может появиться в беспорядочном порядке. Включить
Функция автозапуска Perl, установив $|
на
истинное значение. Обычно вы можете видеть $|++;
в
Программы CGI. После установки каждая печать и запись будут
немедленно перейдите на вывод, а не буферизируйте.
Вы должны установить это для каждого дескриптора файла. Используйте select
для
измените дескриптор файла по умолчанию, например:
$|++; #sets $| for STDOUT
$old_handle = select( STDERR ); #change to STDERR
$|++; #sets $| for STDERR
select( $old_handle ); #change back to STDOUT
В любом случае, первым делом должен быть заголовок CGI за которым следует пустая строка.
Обычно среда веб-сервера намного ограничена чем в командной строке, и имеет дополнительные возможности информацию о запросе. Если ваш script работает нормально из командной строки вы можете попробовать имитировать веб-сервер Окружающая среда. Если проблема возникает, у вас есть проблема окружающей среды.
Отменить или удалить эти переменные
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
Если вы считаете, что ваш script делает правильные вещи, и при выполнении запроса вручную вы получаете право вывода, браузер может быть виновником. Очистить кеш и установите размер кеша равным нулю во время тестирования. Помните, что некоторые браузеры действительно глупы и фактически не перезагружаются новый контент, даже если вы говорите ему об этом. Это особенно распространены в тех случаях, когда URL-путь является но изменение содержимого (например, динамические изображения).
Путь файловой системы к script не обязательно напрямую связанный с URL-адресом пути к script. Убедиться у вас есть правильный каталог, даже если вам нужно написать короткий тест script, чтобы проверить это. Кроме того, уверены ли вы что вы изменяете правильный файл? Если вы не видите любой эффект с вашими изменениями, вы можете изменить другого файла или загрузки файла в неправильное место. (Это, кстати, моя самая частая причина таких неприятностей ;)
CGI.pm
или его производную?Если ваша проблема связана с разбором ввода CGI, и вы
не используют широко проверенный модуль, например CGI.pm
, CGI::Request
,
CGI::Simple
или CGI::Lite
, используйте модуль и продолжайте жить.
CGI.pm
имеет режим совместимости cgi-lib.pl
, который может помочь вам решить ввод
проблемы из-за более старых реализаций CGI-парсера.
Если вы используете внешние команды с
system
, обратные тики или другие объекты МПК,
вы должны использовать абсолютный путь к внешней программе.
Вы не только знаете точно, что вы используете, но вы
избегайте некоторых проблем с безопасностью. Если вы открываете
файлы для чтения или записи, используйте абсолютный путь.
CGI script может иметь другое представление о текущем
чем вы. Кроме того, вы можете сделать
явный chdir()
, чтобы поставить вас в нужное место.
Большинство функций Perl скажут вам, работают ли они или нет
и установит $!
при сбое. Вы проверили
вернуть значение и проверить $!
на сообщения об ошибках? Ты проверил
$@
, если вы использовали eval
?
Последняя стабильная версия Perl - 5.16.2. Используете ли вы более старую версия? Различные версии Perl могут иметь разные представления о предупреждениях.
Различные серверы могут действовать по-разному в одном и том же ситуация. Тот же серверный продукт может действовать по-разному с различные конфигурации. Включите столько как можно в любом запросе о помощи.
Серьезные программисты CGI должны знать о сервера, в том числе не только функции сервера и поведение, но также и локальную конфигурацию. документация для вашего сервера может быть недоступна для вас если вы используете коммерческий продукт. В противном случае документация должна быть на вашем сервере. Если это не так, посмотрите для него в Интернете.
comp.infosystems.www.authoring.cgi
?Вероятно, у кого-то была ваша проблема раньше, и что кто-то (возможно, я) ответил на это в этом телеконференция. Хотя эта новостная группа прошла свой расцвет, собранная мудрость из прошлого иногда может быть полезна.
В больших системах может быть сложно отследить ошибку так как происходит так много вещей. Попробуйте воспроизвести проблему поведение с максимально возможной script. Знание проблемы это большая часть исправления. Это может потребовать много времени, но вы пока не нашли проблему, и у вас заканчиваются варианты.:)
Серьезно. Иногда мы можем быть настолько обернуты проблемой, что мы разработать "сужение перцепции" (туннельное зрение). Взять перерыв, получить чашку кофе или взорвать некоторых плохих парней в [Duke Nukem, Quake, Doom, Halo, COD] может дать вам свежую перспективу, в которой вам нужно вернуться к проблеме.
Думаю, CGI::Debug.
Интересно, почему никто не упомянул опцию PERLDB_OPTS
, называемую RemotePort
; хотя, по общему признанию, в Интернете мало рабочих примеров (RemotePort
даже не упоминается в perldebug) - и это для меня было проблематично придумать этот, но здесь он идет (это пример Linux).
Чтобы сделать правильный пример, сначала мне нужно что-то, что может сделать очень простое моделирование веб-сервера CGI, желательно через одну командную строку. После нахождения Простой веб-сервер командной строки для запуска cgis. (perlmonks.org), я нашел IO:: All - Tiny Web Server для этого теста.
Здесь я буду работать в каталоге /tmp
; CGI script будет /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
, сделанный test.pl
, загружен - и показан - в веб-браузере.
Теперь, чтобы отладить этот script с RemotePort
, сначала нам нужен прослушиватель в сети, через который мы будем взаимодействовать с отладчиком Perl; мы можем использовать инструмент командной строки netcat
(nc
, увидели это здесь: Perl 如何 удаленная отладка?). Итак, сначала запустите прослушиватель netcat
в одном терминале, где он будет блокировать и ждать подключения на порту 7234 (который будет нашим портом отладки):
$ nc -l 7234
Затем мы хотим, чтобы perl
запускался в режиме отладки с помощью RemotePort
, когда был вызван test.pl
(даже в режиме CGI, через сервер). Это можно сделать в Linux, используя следующую "shebang wrapper" script, которая здесь также должна быть в /tmp
и должна быть сделана исполняемой:
cd /tmp
cat > perldbgcall.sh <<'EOF'
#!/bin/bash
PERLDB_OPTS="RemotePort=localhost:7234" perl -d -e "do '$@'"
EOF
chmod +x perldbgcall.sh
Это своего рода сложная вещь - см. shell script - Как я могу использовать переменные среды в моем shebang? - Unix и Linux Stack Exchange. Но трюк здесь, похоже, заключается не в том, чтобы развернуть интерпретатор perl
, который обрабатывает test.pl
- поэтому, как только мы его ударим, мы не exec
, но вместо этого мы называем perl
"явно" и в основном "source <наш > test.pl
script с помощью do
(см. Как запустить Perl script из Perl script?).
Теперь, когда мы имеем perldbgcall.sh
в /tmp
, мы можем изменить файл test.pl
, так что он ссылается на этот исполняемый файл на его строке shebang (вместо обычного интерпретатора 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
и его новый обработчик shebang perldbgcall.sh
находятся в /tmp
; и у нас есть nc
прослушивание отладочных подключений на порту 7234, поэтому мы можем, наконец, открыть другое окно терминала, сменить каталог на /tmp
и запустить однострочный веб-сервер (который будет прослушивать веб-соединения на порту 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
. Однако теперь, когда веб-сервер пытается выполнить script, он сделает это через perldbgcall.sh
shebang - который запустит perl
в режиме удаленного отладчика. Таким образом, выполнение script приостанавливается - и поэтому веб-браузер блокируется, ожидая данных. Теперь мы можем переключиться на терминал 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
(и Enter) для "run" - и script запустится оператор breakpoint ( см. также В perl, какая разница между $DB:: single = 1 и 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 script, является изменение shebang (и, конечно же, наличие оболочки shebang script в качестве исполняемого файла файл в том же каталоге).
Хорошо, надеюсь, что это поможет кому-то - я бы наверняка любил бы наткнуться на это, вместо того, чтобы писать сам. :)
Ура!
Используете ли вы обработчик ошибок во время отладки? Операторы
die
и другие фатальные ошибки во время выполнения и времени компиляции получают
напечатано до STDERR
, которое трудно найти и может быть скомпоновано с
сообщений с других веб-страниц на вашем сайте. Пока вы отлаживаете свои
script, это хорошая идея, чтобы получить фатальные сообщения об ошибках для отображения в вашем
браузер каким-то образом.
Один из способов сделать это - позвонить
use CGI::Carp qw(fatalsToBrowser);
вверху вашего script. Этот вызов установит обработчик $SIG{__DIE__}
(см. perlvar) отображают фатальные ошибки в вашем браузере, добавляя при необходимости допустимый заголовок. Другой отладочный трюк CGI, который я использовал до того, как я когда-либо слышал о CGI::Carp
, был
используйте eval
с объектами DATA
и __END__
на script, чтобы поймать ошибки времени компиляции:
#!/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> возвращает массив, по одной строке на элемент. Затем join соединяет его вместе (соединяя с помощью ''). Наконец, Eval.
Для меня я использую 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");
Честно говоря, вы можете сделать все самое интересное над этим сообщением. ALTHOUGH, самое простое и наиболее проактивное решение, которое я нашел, это просто "распечатать".
В примере: (Нормальный код)
`$somecommand`;
Чтобы узнать, делает ли он то, что я действительно хочу сделать: (Устранение неисправностей)
print "$somecommand";
Вероятно, стоит также упомянуть, что Perl всегда расскажет вам, в какой строке возникает ошибка при выполнении Perl script из командной строки. (Например, сеанс SSH)
Я обычно буду делать это, если все остальное не получится. Я буду использовать SSH на сервере и вручную выполнить Perl script. Например:
% perl myscript.cgi
Если возникнет проблема, Perl расскажет вам об этом. Этот метод отладки устраняет любые проблемы, связанные с разрешением файла или проблемы с веб-браузером или веб-сервером.
Вы можете запустить терминал perl cgi- script в терминале, используя следующую команду
$ perl filename.cgi
Он интерпретирует код и предоставляет результат с кодом HTML. Он будет сообщать об ошибке, если таковой имеется.
perl -c filename
действительно проверяет только синтаксис. Но perl filename
выводит вывод HTML. Хотя нет гарантии, что не будет ошибки 500 CGI, но это хороший первый тест.