Як я можу вирішити свій скрипт 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 як усунення неполадок скриптів Perl CGI . Це не повний посібник з кожної проблеми, з якою ви можете зіткнутися, а також не навчальний посібник зі скручування помилок. Це просто кульмінація мого досвіду налагодження сценаріїв 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 є особливо клопіткими. Ви повинні встановити їх на безпечне значення або зняти їх повністю, як я рекомендую. Ви все одно повинні використовувати абсолютні шляхи. Якщо під час перевірки на скарги скаржиться на щось інше, переконайтеся, що ви зберегли дані. Докладні відомості див. На сторінці man perlsec .

Що відбувається, коли ви запустите його з командного рядка?

Чи виводить сценарій те, що ви очікуєте при запуску з командного рядка? Спочатку виводиться заголовок, а потім - порожній рядок? Пам'ятайте, що це 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, а потім порожній рядок.

Що відбувається, коли ви запускаєте його з командного рядка із середовищем, схожим на 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?

Це корисне використання, але всі хороші плакати або загинули, або розгубилися.

Цілком імовірно, що хтось раніше мав вашу проблему, і хтось (можливо, я) відповів на це в цій групі новин. Хоча ця група новин минула свій розквіт, зібрана мудрість з минулого часом може бути корисною.

Чи можете ви відтворити проблему за допомогою короткого тестового сценарію?

У великих системах виявити помилку може бути важко, оскільки так багато справ відбувається. Спробуйте відтворити проблемну поведінку за допомогою найкоротшого сценарію. Знання проблеми - це більшість виправлень. Це, можливо, забирає багато часу, але ви ще не знайшли проблему і не вистачає варіантів. :)

Ви вирішили піти фільм?

Серйозно. Іноді ми можемо настільки заплутатися в проблемі, що розвиваємо «перцептивне звуження» (тунельне бачення). Перерва, випити чашку кави чи вибуху поганих хлопців у [Герцог Нукем, Квайк, Дум, Ореол] може дати вам нову перспективу, що вам потрібно знову підійти до проблеми.

Ви проголосили проблему?

Серйозно знову. Іноді пояснення проблеми вголос призводить нас до власних відповідей. Поговоріть з пінгвіном (плюшевою іграшкою), оскільки ваші колеги не слухають. Якщо ви зацікавлені в цьому як серйозний інструмент налагодження (і я рекомендую це, якщо ви до цього часу не знайшли проблеми), ви також можете прочитати «Психологія комп’ютерного програмування» .


4
Не соромтесь редагувати мою відповідь, якщо у вас є що додати.
Brian d foy

Здається, ви можете видалити посилання на мета-запитання про CGI. 5.12.1 вважається "стабільним"?
Змія Пліскен

1
Чому б не $|=1замість цього $|++?
reinierpost

Чому $|=1замість $|++? Це насправді не має значення, і навіть тоді $|це магія.
Брайан d foy

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, як запропонував Мікаел S, також є дуже корисним і налаштованим інструментом для цієї мети.


3
@Ether: <DATA>це чарівний файл файлів, який читає поточний сценарій, починаючи з __END__. Приєднання надає йому контекст списку, тому <fh> повертає масив, один рядок на позицію. Потім приєднується, з'єднує його назад (з'єднуючи його з ''). Нарешті, eval.
дероберт

@Ether: Більш читаючим способом написання рядка 2 було б:eval join(q{}, <DATA>);
derobert

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

1
@Ether: Ну, насправді вони обидва працюють у сценарії вищого рівня (згідно зі сторінкою perldata). Але оскільки DATA є кращим, я змінив відповідь.
дероберт

@derobert: дякую за посилання doc; Я не знав про поведінку зворотної сумісності __END__!
Ефір

7

Цікаво, як так, що ніхто не згадав про PERLDB_OPTSназваний варіант RemotePort; Хоча, правда, в Інтернеті не так багато робочих прикладів ( RemotePortнавіть не згадується в perldebug ) - і мені було якось проблематично придумати цей, але ось це йде (це є прикладом Linux).

Щоб зробити належний приклад, спочатку мені було потрібно щось, що може зробити дуже просте моделювання веб-сервера CGI, бажано за допомогою одного командного рядка. Після пошуку простого веб-сервера командного рядка для запуску cgis. (perlmonks.org) , я знайшов IO :: All - крихітний веб-сервер, застосовний для цього тесту.

Тут я буду працювати в /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

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


Тепер, для налагодження цього сценарію RemotePort, спочатку нам потрібен слухач у мережі, за допомогою якого ми будемо взаємодіяти з налагоджувачем Perl; ми можемо використовувати інструмент командного рядка netcat( ncбачили, що тут: Perl Per віддалена налагодження? ). Отже, спочатку запустіть 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

Це якась хитра штука - див. Сценарій оболонки - Як я можу використовувати змінні середовища в моєму шебангу? - Обмін стеками 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 не завжди повідомляє вам номер рядка, де виникає помилка. Він повідомляє вам номер рядка, де він зрозумів, що щось не так. Можливо, помилка вже сталася.
Брайан d foy

0

Ви можете запустити perl cgi-скрипт у терміналі за допомогою команди нижче

 $ perl filename.cgi

Він інтерпретує код і надає результат з кодом HTML. Він повідомить про помилку, якщо така є.


1
На жаль, команда $ perl -c filename.cgi перевіряє синтаксис коду та повідомляє про помилку, якщо така є. Він не буде надавати html-код cgi.
Д.Картікеян

Викликання perl -c filenameдійсно перевірятиме лише синтаксис. Але perl filenameдрукує вихід HTML. Це не гарантія, що помилка 500 CGI не буде, але це перший хороший тест.
Нагев
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.