Как я могу устранить неполадки в моем сценарии Perl CGI?


100

У меня есть сценарий Perl, который не работает, и я не знаю, как начать сужать проблему. Что я могу сделать?


Примечание: я добавляю вопрос, потому что действительно хочу добавить свой очень длинный ответ в Stackoverflow. Я продолжаю ссылаться на него в других ответах, и он заслуживает того, чтобы быть здесь. Не стесняйтесь редактировать мой ответ, если вам есть что добавить.


5
@Evan - я просто хочу сказать, что даже если он не является CW, его все равно можно редактировать - если у вас достаточно кармы; 100 для CW, 2k в противном случае. Итак, теперь у вас есть 2060, и вы сможете редактировать сообщения не в CW.
Марк Грейвелл

1
@Evan, волшебные точки перечислены во всплывающих подсказках в правом столбце здесь: stackoverflow.com/privileges
cjm

Если ваш веб-браузер показывает линейный шум, возможно, вместо этого он печатает скрипт perl. В этом случае см. Stackoverflow.com/questions/2621161/…
Эндрю Гримм

Ответы:


129

Этот ответ предназначен в качестве общей основы для решения проблем со сценариями Perl CGI и первоначально появился на Perlmonks как Troubleshooting Perl CGI Scripts . Это не полное руководство по каждой проблеме, с которой вы можете столкнуться, и не руководство по устранению ошибок. Это просто кульминация моего опыта отладки скриптов CGI в течение двадцати (с лишним!) Лет. Похоже, на этой странице было много разных домов, и я, кажется, забыл о ее существовании, поэтому добавляю ее в StackOverflow. Вы можете присылать мне любые комментарии или предложения по адресу bdfoy@cpan.org. Это также вики сообщества, но не стоит слишком нервничать. :)


Используете ли вы встроенные функции Perl для поиска проблем?

Включите предупреждения, чтобы Perl предупреждал вас о сомнительных частях вашего кода. Вы можете сделать это из командной строки с помощью -wпереключателя, поэтому вам не нужно менять какой-либо код или добавлять прагму в каждый файл:

 % perl -w program.pl

Однако вы должны заставить себя всегда прояснять сомнительный код, добавляя warningsпрагму ко всем своим файлам:

 use warnings;

Если вам нужно больше информации, чем короткое предупреждающее сообщение, используйте diagnosticsпрагму для получения дополнительной информации или посмотрите документацию perldiag :

 use diagnostics;

Вы сначала вывели действительный заголовок CGI?

Сервер ожидает, что первым выводом сценария CGI будет заголовок CGI. Как правило , это может быть так просто , как print "Content-type: text/plain\n\n";и с CGI.pm и его производных, print header(). Некоторые серверы чувствительны к тому, что вывод ошибок (включен STDERR) отображается перед стандартным выводом (включен STDOUT).

Попробуйте отправить ошибки в браузер

Добавить эту строку

 use CGI::Carp 'fatalsToBrowser';

к вашему сценарию. Это также отправляет ошибки компиляции в окно браузера. Обязательно удалите это перед переходом в производственную среду, так как дополнительная информация может представлять угрозу безопасности.

Что написано в журнале ошибок?

Серверы ведут журналы ошибок (по крайней мере, должны). Там должны появиться сообщения об ошибках с сервера и вашего скрипта. Найдите журнал ошибок и посмотрите, что в нем написано. Стандартного места для файлов журналов нет. Посмотрите в конфигурации сервера их местонахождение или спросите администратора сервера. Вы также можете использовать такие инструменты, как CGI :: Carp, чтобы хранить свои собственные файлы журналов.

Какие разрешения у скрипта?

Если вы видите такие ошибки, как «Разрешение отказано» или «Метод не реализован», это, вероятно, означает, что ваш скрипт недоступен для чтения и выполнения пользователем веб-сервера. На разновидностях Unix, изменить режим 755 рекомендуется: chmod 755 filename. Никогда не устанавливайте режим на 777!

Вы пользуетесь use strict?

Помните, что Perl автоматически создает переменные, когда вы их впервые используете. Это функция, но иногда она может вызывать ошибки, если вы неправильно введете имя переменной. Прагма use strictпоможет вам найти такого рода ошибки. Это раздражает, пока вы к этому не привыкнете, но через некоторое время ваше программирование значительно улучшится, и вы сможете свободно совершать разные ошибки.

Компилируется ли сценарий?

Вы можете проверить наличие ошибок компиляции с помощью -c переключателя. Сосредоточьтесь на первых обнаруженных ошибках. Промыть, повторить. Если вы получаете действительно странные ошибки, убедитесь, что в вашем скрипте есть правильные окончания строк. Если вы используете FTP в двоичном режиме, проверяете из CVS или что-то еще, что не обрабатывает перевод конца строки, веб-сервер может увидеть ваш сценарий как одну большую строку. Перенести скрипты Perl в режим ASCII.

Сценарий жалуется на небезопасные зависимости?

Если ваш скрипт жалуется на небезопасные зависимости, вы, вероятно, используете -Tпереключатель для включения режима заражения, что хорошо, поскольку оно позволяет вам передавать непроверенные данные в оболочку. Если он жалуется, значит, он помогает нам писать более безопасные скрипты. Любые данные, поступающие извне программы (т. Е. Из среды), считаются испорченными. Особенно неприятны такие переменные среды, как PATHи LD_LIBRARY_PATH. Вы должны установить для них безопасное значение или полностью отключить их, как я рекомендую. В любом случае вы должны использовать абсолютные пути. Если при проверке на заражение возникает какая-то проблема, убедитесь, что вы не испортили данные. См. Подробности на странице руководства perlsec .

Что происходит, когда вы запускаете его из командной строки?

Выводит ли сценарий то, что вы ожидаете, при запуске из командной строки? Сначала выводится заголовок, а за ним - пустая строка? Помните, что это STDERRможет быть объединено, STDOUT если вы находитесь на терминале (например, в интерактивном сеансе), и из-за буферизации может отображаться в беспорядочном порядке. Включите функцию автозапуска Perl, установив $|значение true. Обычно вы можете видеть это $|++;в программах CGI. После установки каждая печать и запись немедленно отправляется на вывод, а не буферизуется. Вы должны установить это для каждого дескриптора файла. Используйте selectдля изменения дескриптора файла по умолчанию, например:

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

В любом случае первым выводом должен быть заголовок CGI, за которым следует пустая строка.

Что произойдет, если вы запустите его из командной строки в среде, подобной 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

Что происходит после очистки кеша браузера?

Если вы считаете, что ваш скрипт делает правильные вещи, и когда вы выполняете запрос вручную, вы получаете правильный результат, возможно, виноват браузер. Очистите кеш и установите его размер равным нулю во время тестирования. Помните, что некоторые браузеры действительно глупы и на самом деле не будут перезагружать новый контент, даже если вы попросите его сделать это. Это особенно часто встречается в случаях, когда 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 вы используете?

Последняя стабильная версия Perl - 5.28 (или нет, в зависимости от того, когда она в последний раз редактировалась). Вы используете старую версию? В разных версиях Perl могут быть разные идеи предупреждений.

Какой веб-сервер вы используете?

В одной и той же ситуации разные серверы могут действовать по-разному. Один и тот же серверный продукт может работать по-разному в разных конфигурациях. Включите как можно больше этой информации в любой запрос о помощи.

Вы проверяли документацию по серверу?

Серьезные программисты CGI должны знать о сервере как можно больше, включая не только особенности и поведение сервера, но и локальную конфигурацию. Документация для вашего сервера может быть недоступна, если вы используете коммерческий продукт. В противном случае документация должна быть на вашем сервере. Если это не так, поищите его в Интернете.

Вы искали в архивах comp.infosystems.www.authoring.cgi?

Это было полезно, но все хорошие плакаты либо умерли, либо рассеялись.

Вероятно, у кого-то уже была ваша проблема и что кто-то (возможно, я) ответил на нее в этой группе новостей. Хотя эта группа новостей уже пережила свой расцвет, накопленная мудрость из прошлого иногда может быть полезной.

Можете ли вы воспроизвести проблему с помощью короткого тестового сценария?

В больших системах может быть сложно отследить ошибку, поскольку происходит так много всего. Постарайтесь воспроизвести проблемное поведение как можно более коротким сценарием. Знание проблемы - это большая часть решения. Это, безусловно, может занять много времени, но вы еще не нашли проблему и у вас заканчиваются варианты. :)

Вы решили пойти в кино?

Шутки в сторону. Иногда мы можем настолько увлечься проблемой, что у нас развивается «сужение восприятия» (туннельное зрение). Сделав перерыв, выпив чашку кофе или взорвав плохих парней в [Duke Nukem, Quake, Doom, Halo, COD], вы можете по-новому взглянуть на проблему.

Вы озвучили проблему?

Снова серьезно. Иногда объяснение проблемы вслух приводит нас к нашим собственным ответам. Поговорите с пингвином (плюшевая игрушка), потому что ваши коллеги не слушают. Если вы заинтересованы в этом как в серьезном инструменте отладки (и я рекомендую его, если вы еще не нашли проблему), вы также можете прочитать Психологию компьютерного программирования .


4
Не стесняйтесь редактировать мой ответ, если вам есть что добавить.
brian d foy

Похоже, вы могли бы захотеть удалить ссылку на часто задаваемые вопросы о CGI meta. 5.12.1 считается "стабильной"?
Snake Plissken

1
Почему не $|=1вместо $|++?
Reinierpost

Почему $|=1вместо $|++? На самом деле это не имеет значения, и даже тогда $|это волшебно.
Брайан Д Фой

2
Хороший ответ, думаю, стоит упомянуть, что некоторые из этих решений должны быть предназначены исключительно для устранения неполадок, а не вводиться в производственный код. use strictкак правило, полезно использовать в любое время, тогда как использование fatalsToBrowserможет быть не рекомендовано в производственной среде, особенно если вы используете die.
vol7ron


7

Вы используете обработчик ошибок во время отладки?

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, как предложил Микаэль С., также очень полезный и настраиваемый инструмент для этой цели.


3
@Ether: <DATA>волшебный дескриптор файла, который читает текущий скрипт, начиная с __END__. Join предоставляет контекст списка, поэтому <fh> возвращает массив, по одной строке на элемент. Затем join соединяет их (соединяя их с помощью ""). Наконец, eval.
derobert

@Ether: Более читаемый способ записать строку 2 будет:eval join(q{}, <DATA>);
derobert

@derobert: на самом деле, __DATA__ - это токен, используемый для запуска раздела данных, а не __END__ (я думаю, это была моя путаница).
Ether

1
@Ether: На самом деле, они оба работают в скрипте верхнего уровня (согласно man-странице perldata). Но поскольку DATA предпочтительнее, я изменил ответ.
derobert

@derobert: спасибо за ссылку на документ; Я не знал об обратной совместимости __END__!
эфир

7

Интересно, почему никто не упомянул PERLDB_OPTSназванный вариант RemotePort; хотя, по общему признанию, в сети не так много рабочих примеров ( RemotePortдаже не упомянутых в perldebug ) - и мне было довольно проблематично придумать этот, но вот оно (это пример Linux).

Чтобы сделать правильный пример, сначала мне нужно было что-то, что могло бы выполнять очень простую симуляцию веб-сервера CGI, предпочтительно через одну командную строку. После нахождения веб-сервера простой командной строки для запуска cgis. (perlmonks.org) , я обнаружил, что IO :: All - 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 \/(.*) / })'

Команда веб-сервера заблокируется в терминале, а в противном случае запустит веб-сервер локально (на 127.0.0.1 или localhost) - после этого я могу перейти в веб-браузер и запросить этот адрес:

http://127.0.0.1:8080/test.pl

... и я должен наблюдать за prints, создаваемыми test.plпри загрузке - и отображении - в веб-браузере.


Теперь, чтобы отлаживать этот сценарий RemotePort, сначала нам понадобится слушатель в сети, через который мы будем взаимодействовать с отладчиком Perl; мы можем использовать инструмент командной строки netcat(вы ncвидели это здесь: Perl 如何 удаленная отладка? ). Итак, сначала запустите netcatпрослушиватель в одном терминале - где он будет блокировать и ждать соединений на порту 7234 (который будет нашим портом отладки):

$ nc -l 7234

Затем мы хотели бы perlначать в режиме отладки с RemotePort, когда test.plбыл вызван (даже в режиме CGI через сервер). В Linux это можно сделать с помощью следующего сценария «оболочки shebang», который здесь также должен быть включен /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 . Но хитрость здесь , кажется, не раскошелиться на perlинтерпретатор , который ручками test.pl - так , как только мы попали его, мы не делаем exec, но вместо этого мы называем perl«ясно», и в основном «источник» наш test.plскрипт с помощью do(см Как запустить Сценарий Perl из сценария Perl? ).

Теперь, когда мы имеем 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и его новый обработчик 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. Однако теперь, когда веб-сервер пытается выполнить сценарий, он будет делать это через perldbgcall.shshebang, который запускается 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(и Enter) для «запустить» - и скрипт запустится и выполнит оператор точки останова (см. Также В 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, - это изменение 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 сообщит вам об этом. Этот метод отладки устраняет любые проблемы, связанные с правами доступа к файлам, или проблемы с веб-браузером или веб-сервером.


Perl не всегда сообщает вам номер строки, в которой возникает ошибка. Он сообщает вам номер строки, где он понимает, что что-то не так. Вероятно, ошибка уже произошла.
brian d foy

0

Вы можете запустить perl cgi-скрипт в терминале, используя следующую команду

 $ perl filename.cgi

Он интерпретирует код и предоставляет результат с кодом HTML. Он сообщит об ошибке, если таковая имеется.


1
К сожалению, команда $ perl -c filename.cgi проверяет синтаксис кода и сообщает об ошибке, если таковая имеется. Он не предоставляет html-код cgi.
Д.Картикеян 06

Вызов perl -c filenameдействительно проверяет только синтаксис. Но perl filenameраспечатывает вывод HTML. Хотя нет гарантии, что ошибки 500 CGI не будет, но это хороший первый тест.
Nagev
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.