Mam skrypt Perla, który nie działa i nie wiem, jak zawęzić problem. Co mogę zrobić?
Uwaga: dodaję pytanie, ponieważ naprawdę chcę dodać moją bardzo długą odpowiedź do Stackoverflow. Wciąż łączę się z nim w innych odpowiedziach i zasługuje na to, aby tu być. Nie wstydź się edytować mojej odpowiedzi, jeśli masz coś do dodania.
Odpowiedzi:
Ta odpowiedź ma służyć jako ogólne ramy do rozwiązywania problemów ze skryptami Perl CGI i pierwotnie pojawiła się w Perlmonks jako Rozwiązywanie problemów ze skryptami Perl CGI . Nie jest to kompletny przewodnik po każdym napotkanym problemie ani samouczek dotyczący usuwania błędów. To tylko kulminacja mojego doświadczenia w debugowaniu skryptów CGI przez dwadzieścia (plus!) Lat. Wygląda na to, że ta strona miała wiele różnych domów i wydaje mi się, że zapomniałem, że istnieje, więc dodaję ją do StackOverflow. Możesz przesłać mi wszelkie uwagi lub sugestie na adres [email protected]. To także wiki społeczności, ale nie zwariuj. :)
Czy używasz wbudowanych funkcji Perla, aby pomóc Ci znaleźć problemy?
Włącz ostrzeżenia, aby Perl ostrzegał Cię o wątpliwych częściach kodu. Możesz to zrobić z wiersza poleceń za pomocą
-w
przełącznika, więc nie musisz zmieniać żadnego kodu ani dodawać pragmy do każdego pliku:Powinieneś jednak zmusić się do zawsze wyjaśniania wątpliwego kodu, dodając
warnings
pragmę do wszystkich swoich plików:Jeśli potrzebujesz więcej informacji niż krótki komunikat ostrzegawczy, skorzystaj z
diagnostics
pragmy, aby uzyskać więcej informacji, lub zajrzyj do dokumentacji perldiag :Czy najpierw wyprowadziłeś prawidłowy nagłówek CGI?
Serwer oczekuje, że pierwszym wyjściem ze skryptu CGI będzie nagłówek CGI. Typowo, które mogą być proste, jak
print "Content-type: text/plain\n\n";
i z CGI.pm i jego pochodnychprint header()
. Niektóre serwery są wrażliwe na błąd wyjścia (onSTDERR
) pojawiający się przed standardowym wyjściem (onSTDOUT
).Spróbuj wysłać błędy do przeglądarki
Dodaj tę linię
do swojego skryptu. Spowoduje to również wysłanie błędów kompilacji do okna przeglądarki. Pamiętaj, aby usunąć to przed przejściem do środowiska produkcyjnego, ponieważ dodatkowe informacje mogą stanowić zagrożenie dla bezpieczeństwa.
Co zawiera dziennik błędów?
Serwery przechowują dzienniki błędów (a przynajmniej powinny). Powinien tam pojawić się błąd wyjściowy z serwera i skryptu. Znajdź dziennik błędów i zobacz, co zawiera. Nie ma standardowego miejsca na pliki dziennika. Sprawdź w konfiguracji serwera ich lokalizację lub zapytaj administratora serwera. Możesz także użyć narzędzi takich jak CGI :: Carp aby zachować własne pliki dziennika.
Jakie są uprawnienia skryptu?
Jeśli widzisz błędy, takie jak „Odmowa uprawnień” lub „Metoda nie zaimplementowana”, prawdopodobnie oznacza to, że Twój skrypt nie jest czytelny i nie może być wykonywany przez użytkownika serwera WWW. W przypadku wersji Unix zaleca się zmianę trybu na 755:
chmod 755 filename
. Nigdy nie ustawiaj trybu na 777!Czy używasz
use strict
?Pamiętaj, że Perl automatycznie tworzy zmienne, gdy używasz ich po raz pierwszy. Jest to funkcja, ale czasami może powodować błędy, jeśli błędnie wpiszesz nazwę zmiennej. Pragma
use strict
pomoże ci znaleźć tego rodzaju błędy. Jest to denerwujące, dopóki się do tego nie przyzwyczaisz, ale po pewnym czasie programowanie znacznie się poprawi i będziesz mógł popełniać różne błędy.Czy skrypt się kompiluje?
Możesz sprawdzić błędy kompilacji za pomocą
-c
przełącznika. Skoncentruj się na pierwszych zgłoszonych błędach. Spłucz, powtórz. Jeśli otrzymujesz naprawdę dziwne błędy, sprawdź, czy Twój skrypt ma prawidłowe zakończenia linii. Jeśli korzystasz z FTP w trybie binarnym, wyewidencjonujesz z CVS lub coś innego, co nie obsługuje tłumaczenia końca linii, serwer WWW może zobaczyć twój skrypt jako jedną dużą linię. Prześlij skrypty Perla w trybie ASCII.Czy skrypt narzeka na niezabezpieczone zależności?
Jeśli twój skrypt narzeka na niezabezpieczone zależności, prawdopodobnie używasz
-T
przełącznika do włączania trybu skażenia, co jest dobrą rzeczą, ponieważ zapewnia przekazywanie niezaznaczonych danych do powłoki. Jeśli narzeka, wykonuje swoją pracę, pomagając nam pisać bezpieczniejsze skrypty. Wszelkie dane pochodzące spoza programu (tj. Ze środowiska) są uważane za skażone. Zmienne środowiskowe, takie jakPATH
iLD_LIBRARY_PATH
są szczególnie kłopotliwe. Musisz ustawić je na bezpieczną wartość lub całkowicie je wyłączyć, tak jak zalecam. I tak powinieneś używać ścieżek bezwzględnych. Jeśli sprawdzanie skażeń narzeka na coś innego, upewnij się, że dane zostały oczyszczone. Szczegółowe informacje można znaleźć na stronie podręcznika perlsec .Co się stanie, gdy uruchomisz go z wiersza poleceń?
Czy skrypt wyświetla to, czego oczekujesz po uruchomieniu z wiersza poleceń? Czy najpierw wyjście nagłówka, po którym następuje pusty wiersz? Pamiętaj, że
STDERR
może zostać scalone z,STDOUT
jeśli jesteś na terminalu (np. Sesja interaktywna), a ze względu na buforowanie może pojawić się w pomieszanej kolejności. Włącz funkcję autoflush Perla, ustawiając$|
wartość true. Zwykle można to zobaczyć$|++;
w programach CGI. Po ustawieniu każde drukowanie i zapis będzie natychmiast trafiać na wyjście, a nie być buforowane. Musisz to ustawić dla każdego uchwytu pliku. Użyj,select
aby zmienić domyślny uchwyt pliku, na przykład:Tak czy inaczej, pierwszym wyjściem powinien być nagłówek CGI, po którym powinien znajdować się pusty wiersz.
Co się stanie, gdy uruchomisz go z wiersza poleceń w środowisku podobnym do CGI?
Środowisko serwera WWW jest zwykle dużo bardziej ograniczone niż środowisko wiersza poleceń i zawiera dodatkowe informacje o żądaniu. Jeśli twój skrypt działa dobrze z wiersza poleceń, możesz spróbować zasymulować środowisko serwera WWW. Jeśli pojawi się problem, masz problem ze środowiskiem.
Cofnij ustawienie lub usuń te zmienne
PATH
LD_LIBRARY_PATH
ORACLE_*
zmienneUstaw te zmienne
REQUEST_METHOD
(wartośćGET
,HEAD
lubPOST
, jak w tym przypadku)SERVER_PORT
(zwykle ustawione na 80)REMOTE_USER
(jeśli robisz rzeczy z chronionym dostępem)Najnowsze wersje
CGI.pm
(> 2.75) wymagają-debug
flagi, aby uzyskać stare (przydatne) zachowanie, więc może być konieczne dodanie jej doCGI.pm
importu.Czy używasz
die()
lubwarn
?Te funkcje są drukowane,
STDERR
chyba że zostały przedefiniowane. Nie wyświetlają też nagłówka CGI. Możesz uzyskać tę samą funkcjonalność dzięki pakietom takim jak CGI :: CarpCo się dzieje po wyczyszczeniu pamięci podręcznej przeglądarki?
Jeśli uważasz, że twój skrypt działa właściwie, a gdy wykonujesz żądanie ręcznie, otrzymasz odpowiednie dane wyjściowe, przyczyną może być przeglądarka. Wyczyść pamięć podręczną i ustaw jej rozmiar na zero podczas testowania. Pamiętaj, że niektóre przeglądarki są naprawdę głupie i nie ładują ponownie nowej zawartości, mimo że każesz im to zrobić. Jest to szczególnie powszechne w przypadkach, gdy ścieżka adresu URL jest taka sama, ale zmienia się zawartość (np. Dynamiczne obrazy).
Czy scenariusz jest tam, gdzie myślisz?
Ścieżka systemu plików do skryptu niekoniecznie jest bezpośrednio związana ze ścieżką adresu URL do skryptu. Upewnij się, że masz właściwy katalog, nawet jeśli musisz napisać krótki skrypt testowy, aby to przetestować. Ponadto, czy na pewno modyfikujesz właściwy plik? Jeśli nie widzisz żadnego efektu swoich zmian, być może modyfikujesz inny plik lub przesyłasz plik w niewłaściwe miejsce. (Nawiasem mówiąc, to moja najczęstsza przyczyna takich kłopotów;)
Używasz tego
CGI.pm
czy jest to pochodna?Jeśli problem jest związany z parsowania wejście CGI i nie używasz szeroko przetestowany moduł jak
CGI.pm
,CGI::Request
,CGI::Simple
lubCGI::Lite
użyć modułu i zabrać się za życia.CGI.pm
macgi-lib.pl
tryb zgodności, który może pomóc w rozwiązywaniu problemów z wejściem ze względu na starsze implementacje parsera CGI.Czy korzystałeś ze ścieżek bezwzględnych?
Jeśli używasz poleceń zewnętrznych z
system
zaznaczeniami wstecznymi lub innymi udogodnieniami IPC, powinieneś użyć bezwzględnej ścieżki do programu zewnętrznego. Nie tylko wiesz dokładnie, co uruchamiasz, ale także unikasz niektórych problemów związanych z bezpieczeństwem. Jeśli otwierasz pliki do odczytu lub zapisu, użyj ścieżki bezwzględnej. Skrypt CGI może mieć inne pojęcie o bieżącym katalogu niż ty. Alternatywnie możesz zrobić wyraźną wiadomość,chdir()
aby umieścić Cię we właściwym miejscu.Czy sprawdziłeś wartości zwracane?
Większość funkcji Perla powie ci, czy zadziałały, czy nie, i ustawią się
$!
w przypadku awarii. Czy sprawdziłeś wartość zwracaną i sprawdziłeś$!
komunikaty o błędach? Czy sprawdziłeś,$@
czy używałeśeval
?Której wersji Perla używasz?
Najnowsza stabilna wersja Perla to 5.28 (lub nie, w zależności od tego, kiedy była ostatnio edytowana). Czy używasz starszej wersji? Różne wersje Perla mogą mieć różne koncepcje ostrzeżeń.
Z jakiego serwera WWW korzystasz?
W tej samej sytuacji różne serwery mogą działać inaczej. Ten sam produkt serwerowy może działać inaczej w różnych konfiguracjach. Uwzględnij jak najwięcej tych informacji w każdej prośbie o pomoc.
Czy sprawdziłeś dokumentację serwera?
Poważni programiści CGI powinni wiedzieć jak najwięcej o serwerze - w tym nie tylko o funkcjach i zachowaniu serwera, ale także o lokalnej konfiguracji. Dokumentacja serwera może nie być dostępna, jeśli używasz produktu komercyjnego. W przeciwnym razie dokumentacja powinna znajdować się na serwerze. Jeśli tak nie jest, poszukaj go w sieci.
Przeszukałeś archiwa
comp.infosystems.www.authoring.cgi
?To zastosowanie ma być przydatne, ale wszystkie dobre plakaty albo umarły, albo odeszły.
Jest prawdopodobne, że ktoś miał już wcześniej twój problem i że ktoś (prawdopodobnie ja) odpowiedział na niego w tej grupie dyskusyjnej. Chociaż ta grupa dyskusyjna minęła swój okres świetności, zebrana wiedza z przeszłości może czasami być przydatna.
Czy możesz odtworzyć problem za pomocą krótkiego skryptu testowego?
W dużych systemach może być trudno wyśledzić błąd, ponieważ dzieje się tak wiele rzeczy. Spróbuj odtworzyć problematyczne zachowanie za pomocą możliwie najkrótszego skryptu. Znajomość problemu to większość rozwiązania. Może to być z pewnością czasochłonne, ale nie znalazłeś jeszcze problemu i wyczerpują się opcje. :)
Zdecydowałeś się obejrzeć film?
Poważnie. Czasami możemy być tak pochłonięci problemem, że rozwijamy „zwężenie percepcyjne” (widzenie tunelowe). Przerwa, wypicie filiżanki kawy lub zabicie złych facetów w [Duke Nukem, Quake, Doom, Halo, COD] może dać ci świeżą perspektywę, której potrzebujesz, aby ponownie podejść do problemu.
Czy wypowiedziałeś problem?
Poważnie znowu. Czasami głośne wyjaśnienie problemu prowadzi nas do własnych odpowiedzi. Porozmawiaj z pingwinem (pluszowa zabawka), ponieważ Twoi współpracownicy nie słuchają. Jeśli interesuje Cię to jako poważne narzędzie do debugowania (i polecam je, jeśli do tej pory nie znalazłeś problemu), możesz również przeczytać Psychologię programowania komputerowego .
źródło
$|=1
zamiast$|++
?$|=1
zamiast$|++
? To naprawdę nie robi różnicy, a nawet wtedy$|
jest magiczne.use strict
jest ogólnie dobry w użyciu przez cały czas, podczas gdy używaniefatalsToBrowser
może nie być zalecane w produkcji, zwłaszcza jeśli używaszdie
.Myślę, że warto wspomnieć o CGI :: Debug .
źródło
die
instrukcje i inne krytyczne błędy czasu wykonywania i kompilacji są drukowaneSTDERR
, co może być trudne do znalezienia i może być łączone z wiadomościami z innych stron internetowych w Twojej witrynie. Podczas debugowania skryptu warto w jakiś sposób wyświetlić komunikaty o błędach krytycznych w przeglądarce.Jednym ze sposobów jest zadzwonienie
u góry skryptu. To wywołanie zainstaluje program
$SIG{__DIE__}
obsługi (zobacz perlvar ) wyświetli krytyczne błędy w przeglądarce, dodając do niego poprawny nagłówek, jeśli to konieczne. Inną sztuczką związaną z debugowaniem CGI, której użyłem, zanim kiedykolwiek usłyszałem,CGI::Carp
było użycieeval
funkcjiDATA
i__END__
w skrypcie do wychwytywania błędów w czasie kompilacji:Ta bardziej szczegółowa technika ma niewielką przewagę nad
CGI::Carp
, ponieważ wyłapuje więcej błędów w czasie kompilacji.Aktualizacja: Nigdy go nie używałem, ale wygląda na to, że
CGI::Debug
, jak sugerował Mikael S, jest również bardzo przydatnym i konfigurowalnym narzędziem do tego celu.źródło
<DATA>
to magiczny uchwyt pliku, który czyta bieżący skrypt, zaczynając od__END__
. Join dostarcza kontekst listy, więc <fh> zwraca tablicę, po jednym wierszu na element. Następnie złączenie składa go z powrotem (łącząc go za pomocą „”). Wreszcie eval.eval join(q{}, <DATA>);
Zastanawiam się, dlaczego nikt nie wspomniał o
PERLDB_OPTS
opcji nazwanejRemotePort
; chociaż trzeba przyznać, nie ma wielu działających przykładów w Internecie (RemotePort
nie ma nawet wzmianki o nich w perldebug ) - i było dla mnie trochę problematyczne wymyślenie tego, ale oto jest (jest to przykład Linuksa).Aby zrobić dobry przykład, najpierw potrzebowałem czegoś, co może wykonać bardzo prostą symulację serwera WWW CGI, najlepiej za pomocą jednej linii poleceń. Po znalezieniu prostego serwera WWW wiersza poleceń do uruchamiania cgis. (perlmonks.org) , znalazłem IO :: All - A Tiny Web Server do zastosowania w tym teście.
Tutaj będę pracować w
/tmp
katalogu; skrypt CGI zostanie/tmp/test.pl
(dołączony poniżej). Zauważ, żeIO::All
serwer będzie obsługiwał tylko pliki wykonywalne w tym samym katalogu co CGI, więcchmod +x test.pl
jest to wymagane tutaj. Tak więc, aby wykonać zwykły test CGI, zmieniam katalog na/tmp
w terminalu i uruchamiam tam jednoliniowy serwer sieciowy:Polecenie serwera sieciowego zablokuje się w terminalu, aw innym przypadku uruchomi serwer WWW lokalnie (na 127.0.0.1 lub
localhost
) - potem mogę przejść do przeglądarki internetowej i zażądać tego adresu:... i powinienem obserwować
print
s wykonane przeztest.pl
załadowanie - i wyświetlenie - w przeglądarce internetowej.Teraz, aby debugować ten skrypt
RemotePort
, najpierw potrzebujemy odbiornika w sieci, przez który będziemy współpracować z debugerem Perla; możemy użyć narzędzia wiersza poleceńnetcat
(nc
widziałeś to tutaj: Perl 如何 zdalny debug? ). Więc najpierw uruchomnetcat
nasłuchiwanie w jednym terminalu - gdzie będzie blokował i czekał na połączenia na porcie 7234 (który będzie naszym portem debugowania):Następnie chcielibyśmy
perl
rozpocząć w trybie debugowaniaRemotePort
, gdytest.pl
został wywołany (nawet w trybie CGI, za pośrednictwem serwera). W Linuksie można to zrobić za pomocą następującego skryptu "shebang wrapper" - który tutaj również musi być w programie/tmp
i musi być wykonany:To trochę podchwytliwe - zobacz skrypt powłoki - Jak mogę używać zmiennych środowiskowych w moim shebangu? - Wymiana stosów systemów Unix i Linux . Wydaje się jednak, że sztuczka polega na tym, aby nie rozwidlać
perl
interpretera, który obsługujetest.pl
- więc gdy już to zrobimy, nie robimy tegoexec
, ale zamiast tego nazywamyperl
„wyraźnie” i zasadniczo „źródłem” naszegotest.pl
skryptu, używającdo
(zobacz Jak uruchomić Skrypt Perla z poziomu skryptu Perla? ).Teraz, gdy mamy już
perldbgcall.sh
w/tmp
- możemy zmienićtest.pl
plik tak, aby odnosił się do tego pliku wykonywalnego w swojej linii shebang (zamiast zwykłego interpretera Perla) - tutaj jest/tmp/test.pl
zmodyfikowany w ten sposób:Teraz, zarówno, jak
test.pl
i jej nowy przewodnikperldbgcall.sh
, są w środku/tmp
; inc
nasłuchujemy połączeń debugowania na porcie 7234 - więc możemy w końcu otworzyć inne okno terminala, zmienić katalog na/tmp
i uruchomić jednoliniowy serwer sieciowy (który będzie nasłuchiwał połączeń internetowych na porcie 8080):Po wykonaniu tych czynności możemy przejść do naszej przeglądarki internetowej i żądać tego samego adresu
http://127.0.0.1:8080/test.pl
. Jednak teraz, gdy serwer sieciowy spróbuje wykonać skrypt, zrobi to poprzezperldbgcall.sh
shebang - który uruchomi sięperl
w trybie zdalnego debugera. W ten sposób wykonanie skryptu zostanie wstrzymane - a zatem przeglądarka internetowa zablokuje się, czekając na dane. Możemy teraz przełączyć się nanetcat
terminal i powinniśmy zobaczyć znajomy tekst debuggera Perla - jednak wyprowadzony przeznc
:Jak widać we fragmencie, używamy teraz zasadniczo
nc
jako „terminala” - więc możemy wpisaćr
(i Enter) dla „run” - a skrypt uruchomi się, wykonując instrukcję punktu przerwania (zobacz także W perlu, jaka jest różnica między $ DB :: single = 1 i 2? ), Przed ponownym zatrzymaniem (uwaga w tym momencie przeglądarka nadal się blokuje).Więc teraz możemy, powiedzmy, przejść przez resztę
test.pl
, przeznc
terminal:... jednak również w tym momencie przeglądarka blokuje się i czeka na dane. Dopiero po wyjściu z debuggera za pomocą
q
:... czy przeglądarka przestaje się blokować - i na końcu wyświetla (kompletne) wyjście
test.pl
:Oczywiście tego rodzaju debugowanie można przeprowadzić nawet bez uruchamiania serwera WWW - jednak fajną rzeczą jest to, że w ogóle nie dotykamy serwera WWW; uruchamiamy wykonanie „natywnie” (dla CGI) z przeglądarki internetowej - a jedyną zmianą potrzebną w samym skrypcie CGI jest zmiana shebang (i oczywiście obecność skryptu shebang wrapper, jako pliku wykonywalnego w tym samym informator).
Cóż, mam nadzieję, że to pomoże kogoś - na pewno by się spodobało, że natknęliśmy się na to, zamiast pisać to sam
:)
Cheers!
źródło
U mnie używam log4perl . Jest to całkiem przydatne i łatwe.
źródło
Szczerze mówiąc, możesz robić wszystkie fajne rzeczy powyżej tego postu. Chociaż najprostszym i najbardziej proaktywnym rozwiązaniem, jakie znalazłem, było po prostu „wydrukowanie”.
Na przykład: (Normalny kod)
Aby sprawdzić, czy robi to, czego naprawdę chcę: (Rozwiązywanie problemów)
źródło
Prawdopodobnie warto również wspomnieć, że Perl zawsze powie ci, w której linii wystąpił błąd, gdy wykonujesz skrypt Perla z wiersza poleceń. (Na przykład sesja SSH)
Zwykle robię to, jeśli wszystko inne zawiedzie. Będę SSH na serwerze i ręcznie uruchomię skrypt Perla. Na przykład:
Jeśli wystąpi problem, Perl Ci o tym poinformuje. Ta metoda debugowania eliminuje wszelkie problemy związane z uprawnieniami do plików lub problemy z przeglądarką internetową lub serwerem WWW.
źródło
Możesz uruchomić skrypt perl cgi-script w terminalu za pomocą poniższego polecenia
Interpretuje kod i podaje wynik w postaci kodu HTML. Zgłosi błąd, jeśli wystąpi.
źródło
perl -c filename
rzeczywiście sprawdzi tylko składnię. Aleperl filename
drukuje wyjście HTML. Nie ma jednak gwarancji, że nie wystąpi błąd 500 CGI, ale to dobry pierwszy test.