¿Cómo puedo solucionar mi script Perl CGI?
(8)
Tengo un script de Perl que no funciona y no sé cómo comenzar a reducir el problema. ¿Que puedo hacer?
Nota: Estoy agregando la pregunta porque realmente quiero agregar mi respuesta muy larga a Stackoverflow. Me mantengo vinculándolo externamente en otras respuestas y merece estar aquí. No tenga miedo de editar mi respuesta si tiene algo que agregar.
Creo que CGI::Debug vale la pena mencionarlo.
Honestamente, puedes hacer todas las cosas divertidas por encima de esta publicación. AUNQUE, la solución más simple y proactiva que encontré fue simplemente "imprimirla".
En el ejemplo: (código normal)
`$somecommand`;
Para ver si está haciendo lo que realmente quiero que haga: (Resolución de problemas)
print "$somecommand";
Me pregunto cómo es que nadie mencionó la opción RemotePort
llamada RemotePort
; aunque es cierto que no hay muchos ejemplos que funcionen en la web ( RemotePort
ni siquiera se menciona en perldebug ), y fue un poco problemático para mí presentar este, pero aquí va (es un ejemplo de Linux).
Para hacer un buen ejemplo, primero necesitaba algo que pueda hacer una simulación muy simple de un servidor web CGI, preferentemente a través de una sola línea de comando. Después de encontrar el servidor web Simple command line para ejecutar cgis. (perlmonks.org) , encontré que IO :: All - A Tiny Web Server es aplicable para esta prueba.
Aquí, trabajaré en el directorio /tmp
; el script CGI será /tmp/test.pl
(incluido a continuación). Tenga en cuenta que el servidor IO::All
solo publicará archivos ejecutables en el mismo directorio que CGI, por chmod +x test.pl
se requiere chmod +x test.pl
aquí. Entonces, para hacer la ejecución habitual de la prueba CGI, cambio el directorio a /tmp
en la terminal y ejecuto el servidor web de una sola línea allí:
$ cd /tmp
$ perl -MIO::All -e ''io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET //(.*) / })''
El comando del servidor web se bloqueará en el terminal, y de lo contrario iniciará el servidor web localmente (en 127.0.0.1 o localhost
) - luego, puedo ir a un navegador web y solicitar esta dirección:
http://127.0.0.1:8080/test.pl
... y debería observar las print
realizadas por test.pl
se cargan, y se muestran, en el navegador web.
Ahora, para depurar este script con RemotePort
, primero necesitamos un oyente en la red, a través del cual interactuaremos con el depurador de Perl; podemos usar la herramienta de línea de comandos netcat
( nc
, vimos que aquí: Perl 如何 remote debug? ). Entonces, primero ejecute el oyente netcat
en un terminal, donde bloqueará y esperará las conexiones en el puerto 7234 (que será nuestro puerto de depuración):
$ nc -l 7234
Entonces, RemotePort
perl
comenzara en modo de depuración con RemotePort
, cuando el test.pl
ha sido llamado (incluso en modo CGI, a través del servidor). Esto, en Linux, se puede hacer usando el siguiente script "shebang wrapper", que aquí también debe estar en /tmp
, y debe hacerse ejecutable:
cd /tmp
cat > perldbgcall.sh <<''EOF''
#!/bin/bash
PERLDB_OPTS="RemotePort=localhost:7234" perl -d -e "do ''$@''"
EOF
chmod +x perldbgcall.sh
Esto es algo complicado - ver el script de shell - ¿Cómo puedo usar variables de entorno en mi shebang? - Unix y Linux Stack Exchange . Pero, el truco aquí parece ser no bifurcar al intérprete de perl
que maneja test.pl
, así que una vez que lo alcanzamos, no exec
, sino que llamamos a perl
"claramente", y básicamente "fuente" nuestro test.pl
script utilizando do
(ver ¿Cómo ejecuto un script Perl desde un script Perl? ).
Ahora que tenemos perldbgcall.sh
en /tmp
, podemos cambiar el archivo test.pl
, de modo que haga referencia a este archivo ejecutable en su línea shebang (en lugar del intérprete habitual de Perl) - aquí está /tmp/test.pl
modificado así:
#!./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";
Ahora, tanto test.pl
como su nuevo controlador shebang, perldbgcall.sh
, están en /tmp
; y tenemos que escuchar las conexiones de depuración en el puerto 7234, así que finalmente podemos abrir otra ventana de terminal, cambiar el directorio a /tmp
y ejecutar el servidor web de una línea (que escuchará las conexiones web en el puerto 8080) allí:
cd /tmp
perl -MIO::All -e ''io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET //(.*) / })''
Una vez hecho esto, podemos acceder a nuestro navegador web y solicitar la misma dirección, http://127.0.0.1:8080/test.pl
. Sin embargo, ahora cuando el servidor web intente ejecutar el script, lo hará a través de perldbgcall.sh
shebang, que iniciará perl
en el modo de depuración remota. Por lo tanto, la ejecución de la secuencia de comandos se detendrá, por lo que el navegador web se bloqueará, esperando datos. Ahora podemos cambiar al terminal de netcat
, y deberíamos ver el conocido texto del depurador de Perl, sin embargo, la salida a través de 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>
Como muestra el fragmento, ahora básicamente usamos nc
como un "terminal" - entonces podemos escribir r
(y Enter) para "ejecutar" - y el script se ejecutará en la declaración del punto de interrupción (ver también En Perl, ¿cuál es la diferencia? entre $ DB :: single = 1 y 2? ), antes de volver a detenerse (tenga en cuenta que en ese punto, el navegador aún se bloqueará).
Entonces, ahora podemos, digamos, pasar por el resto de test.pl
, a través del terminal 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>
... sin embargo, también en este punto, el navegador bloquea y espera datos. Solo después de salir del depurador con q
:
DB<1> q
$
... el navegador detiene el bloqueo y finalmente muestra la salida (completa) de test.pl
:
YEAH hello 2 there CMON
CMON hello 3 there YEAH
STEP hello 4 there NOW
STEP hello 5 there AGAIN
Por supuesto, este tipo de depuración se puede realizar incluso sin ejecutar el servidor web; sin embargo, lo mejor de todo es que no tocamos el servidor web en absoluto; activamos la ejecución "nativamente" (para CGI) desde un navegador web, y el único cambio necesario en el script CGI es el cambio de shebang (y por supuesto, la presencia del script shebang wrapper, como archivo ejecutable en el mismo directorio).
Bueno, espero que esto ayude a alguien, seguramente me hubiera encantado haber tropezado con esto, en lugar de escribirlo yo mismo :)
¡Aclamaciones!
Para mí, uso log4perl . Es bastante útil y fácil.
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");
Probablemente también valga la pena mencionar que Perl siempre te dirá en qué línea se produce el error cuando ejecutas el script de Perl desde la línea de comando. (Una sesión SSH, por ejemplo)
Habitualmente haré esto si todo lo demás falla. Introduciré SSH en el servidor y ejecutaré manualmente el script de Perl. Por ejemplo:
% perl myscript.cgi
Si hay un problema, Perl te lo contará. Este método de depuración elimina cualquier problema relacionado con permisos de archivos o problemas con el navegador web o el servidor web.
Puede ejecutar perl cgi-script en la terminal usando el siguiente comando
$ perl filename.cgi
Interpreta el código y proporciona el resultado con código HTML. Informará el error si lo hay.
Esta respuesta pretende ser un marco general para resolver los problemas con los scripts Perl CGI y apareció originalmente en Perlmonks como solución de problemas de secuencias de comandos Perl CGI . No es una guía completa de todos los problemas que pueda encontrar, ni un tutorial sobre cómo solucionar errores. Es solo la culminación de mi experiencia en la depuración de scripts CGI durante diez (¡más!) Años. Esta página parece haber tenido muchos hogares diferentes, y parece que olvido que existe, así que lo estoy agregando al . Puede enviarme sus comentarios o sugerencias a [email protected]. También es wiki de la comunidad, pero no te vuelvas loco. :)
¿Estás usando las funciones integradas de Perl para ayudarte a encontrar problemas?
Active las advertencias para permitir que Perl lo advierta sobre partes cuestionables de su código. Puede hacer esto desde la línea de comandos con el modificador -w
para no tener que cambiar ningún código o agregar un pragma a cada archivo:
% perl -w program.pl
Sin embargo, debe obligarse a borrar siempre el código cuestionable agregando las warnings
pragma a todos sus archivos:
use warnings;
Si necesita más información que el breve mensaje de advertencia, use el pragma de diagnostics
para obtener más información, o consulte la documentación de perldiag :
use diagnostics;
¿Salió un encabezado CGI válido primero?
El servidor espera que el primer resultado de un script CGI sea el encabezado CGI. Normalmente, esto podría ser tan simple como print "Content-type: text/plain/n/n";
o con CGI.pm y sus derivados, print header()
. Algunos servidores son sensibles a la salida de error (en STDERR
) que aparece antes de la salida estándar (en STDOUT
).
Intente enviar errores al navegador
Agrega la linea
use CGI::Carp ''fatalsToBrowser'';
a tu script Esto también envía errores de compilación a la ventana del navegador. Asegúrese de eliminar esto antes de pasar a un entorno de producción, ya que la información adicional puede ser un riesgo para la seguridad.
¿Qué dice el registro de errores?
Los servidores mantienen registros de errores (o deberían, al menos). La salida de error del servidor y de su script debería aparecer allí. Encuentra el registro de errores y mira lo que dice. No hay un lugar estándar para los archivos de registro. Busque en la configuración del servidor su ubicación o pregunte al administrador del servidor. También puede usar herramientas como CGI::Carp para guardar sus propios archivos de registro.
¿Cuáles son los permisos del guión?
Si ve errores como "Permiso denegado" o "Método no implementado", probablemente signifique que el script no es legible y ejecutable por el usuario del servidor web. En los sabores de Unix, se recomienda cambiar el modo a 755: chmod 755 filename
. ¡Nunca configure un modo en 777!
¿Estás usando el use strict
?
Recuerde que Perl crea automáticamente variables cuando las usa por primera vez. Esta es una característica, pero a veces puede causar errores si escribe mal un nombre de variable. El use strict
pragma te ayudará a encontrar ese tipo de errores. Es molesto hasta que te acostumbras, pero tu programación mejorará significativamente después de un tiempo y serás libre de cometer diferentes errores.
¿El script se compila?
Puede verificar los errores de compilación utilizando el -c
. Concéntrese en los primeros errores reportados. Enjuague, repita. Si obtiene errores realmente extraños, asegúrese de que su secuencia de comandos tenga las terminaciones de línea correctas. Si realiza un FTP en modo binario, finaliza la compra desde CVS, o alguna otra cosa que no maneje la traducción de línea final, el servidor web puede ver su secuencia de comandos como una gran línea. Transfiera los guiones Perl en modo ASCII.
¿El script se queja de dependencias inseguras?
Si su secuencia de comandos se queja de dependencias inseguras, probablemente esté utilizando el interruptor -T
para activar el modo de eliminación de manchas, lo cual es bueno, ya que evita tener que pasar datos no verificados al shell. Si se queja, está haciendo su trabajo para ayudarnos a escribir scripts más seguros. Cualquier dato que se origine fuera del programa (es decir, el entorno) se considera contaminado. Las variables de entorno como PATH
y LD_LIBRARY_PATH
son particularmente problemáticas. Debe establecerlos en un valor seguro o desarmarlos por completo, como recomiendo. Deberías estar usando rutas absolutas de todos modos. Si el control de la contaminación se queja de otra cosa, asegúrese de haber eliminado los datos. Consulte la página del manual de perlsec para más detalles.
¿Qué sucede cuando lo ejecutas desde la línea de comando?
¿El script da como resultado lo que espera cuando se ejecuta desde la línea de comando? ¿El resultado del encabezado es primero, seguido de una línea en blanco? Recuerde que STDERR
puede combinarse con STDOUT
si se encuentra en una terminal (por ejemplo, una sesión interactiva), y debido al almacenamiento en búfer puede aparecer en un orden desordenado. Active la función de autoflush de Perl estableciendo $|
a un verdadero valor. Normalmente, puede ver $|++;
en programas CGI. Una vez configurado, cada impresión y escritura irá inmediatamente a la salida en lugar de almacenarse en el búfer. Tienes que configurar esto para cada identificador de archivo. Use select
para cambiar el identificador de archivo predeterminado, de esta manera:
$|++; #sets $| for STDOUT
$old_handle = select( STDERR ); #change to STDERR
$|++; #sets $| for STDERR
select( $old_handle ); #change back to STDOUT
De cualquier manera, la primera salida debe ser el encabezado CGI seguido de una línea en blanco.
¿Qué sucede cuando lo ejecutas desde la línea de comando con un entorno similar a CGI?
El entorno del servidor web suele ser mucho más limitado que el entorno de línea de comandos y tiene información adicional sobre la solicitud. Si su script funciona bien desde la línea de comandos, puede tratar de simular un entorno de servidor web. Si aparece el problema, tienes un problema de entorno.
Desactivar o eliminar estas variables
-
PATH
-
LD_LIBRARY_PATH
- todas las variables
ORACLE_*
Establecer estas variables
-
REQUEST_METHOD
(configurado paraGET
,HEAD
oPOST
según corresponda) -
SERVER_PORT
(establecido en 80, por lo general) -
REMOTE_USER
(si está haciendo cosas de acceso protegido)
Las versiones recientes de CGI.pm
(> 2.75) requieren el -debug
para obtener el comportamiento anterior (útil), por lo que es posible que tenga que agregarlo a sus importaciones CGI.pm
use CGI qw(-debug)
¿Estás usando die()
o warn
?
Esas funciones se imprimen en STDERR
menos que las haya redefinido. Tampoco producen un encabezado CGI. Puede obtener la misma funcionalidad con paquetes como CGI::Carp
¿Qué sucede después de borrar el caché del navegador?
Si cree que su script está haciendo lo correcto, y cuando realiza la solicitud manualmente obtendrá el resultado correcto, el navegador podría ser el culpable. Borre la memoria caché y establezca el tamaño de la memoria caché en cero durante la prueba. Recuerde que algunos navegadores son realmente estúpidos y no volverán a cargar contenido nuevo, aunque usted así lo indique. Esto es especialmente frecuente en los casos en que la ruta de la URL es la misma, pero el contenido cambia (por ejemplo, imágenes dinámicas).
¿Es el guión donde crees que es?
La ruta del sistema de archivos a una secuencia de comandos no está necesariamente relacionada directamente con la ruta de la URL a la secuencia de comandos. Asegúrese de tener el directorio correcto, incluso si tiene que escribir un breve script de prueba para probarlo. Además, ¿está seguro de que está modificando el archivo correcto? Si no ve ningún efecto con sus cambios, es posible que esté modificando un archivo diferente o cargando un archivo en el lugar equivocado. (Esta es, por cierto, mi causa más frecuente de tales problemas;)
¿Estás usando CGI.pm
, o un derivado de él?
Si su problema está relacionado con el análisis sintáctico de la entrada CGI y no está utilizando un módulo ampliamente probado como CGI.pm
, CGI::Request
, CGI::Simple
o CGI::Lite
, use el módulo y CGI.pm
con la vida. CGI.pm
tiene un modo de compatibilidad cgi-lib.pl
que puede ayudarlo a resolver problemas de entrada debido a implementaciones de analizador CGI más antiguas.
¿Usaste caminos absolutos?
Si está ejecutando comandos externos con system
, marcas de retroceso u otras facilidades de IPC, debe usar una ruta absoluta al programa externo. No solo sabe exactamente qué está ejecutando, sino que también evita algunos problemas de seguridad. Si está abriendo archivos para leer o escribir, use una ruta absoluta. La secuencia de comandos CGI puede tener una idea diferente sobre el directorio actual que usted. Alternativamente, puede hacer un chdir()
explícito para ponerlo en el lugar correcto.
¿Revisaste tus valores devueltos?
¡La mayoría de las funciones de Perl le indicarán si funcionó o no y establecerán $!
en el fracaso ¿Revisaste el valor de retorno y examinaste $!
para mensajes de error? ¿Marcó $@
si usaba eval
?
¿Qué versión de Perl estás usando?
La última versión estable de Perl es 5.16.2. ¿Estás usando una versión anterior? Las diferentes versiones de Perl pueden tener diferentes ideas de advertencias.
¿Qué servidor web estás usando?
Diferentes servidores pueden actuar de manera diferente en la misma situación. El mismo producto de servidor puede actuar de manera diferente con diferentes configuraciones. Incluya tanta información como pueda en cualquier solicitud de ayuda.
¿Revisaste la documentación del servidor?
Los programadores de CGI serios deben saber todo lo que se pueda sobre el servidor, incluyendo no solo las características y el comportamiento del servidor, sino también la configuración local. Es posible que la documentación de su servidor no esté disponible para usted si está utilizando un producto comercial. De lo contrario, la documentación debe estar en su servidor. Si no es así, búscalo en la web.
¿Buscó los archivos de comp.infosystems.www.authoring.cgi
?
Es probable que alguien haya tenido su problema anteriormente y que alguien (posiblemente yo) haya respondido en este grupo de noticias. Aunque este grupo de noticias ha pasado su apogeo, la sabiduría acumulada del pasado a veces puede ser útil.
¿Puedes reproducir el problema con un pequeño script de prueba?
En sistemas grandes, puede ser difícil rastrear un error ya que están sucediendo tantas cosas. Intenta reproducir el comportamiento problemático con la secuencia de comandos más corta posible. Saber el problema es la mayor parte de la solución. Esto puede llevar mucho tiempo, pero aún no ha encontrado el problema y se está quedando sin opciones. :)
¿Decidiste ir a ver una película?
Seriamente. A veces podemos estar tan absortos en el problema que desarrollamos un "estrechamiento perceptual" (visión de túnel). Tomar un descanso, tomar una taza de café o bombardear a algunos tipos malos en [Duke Nukem, Quake, Doom, Halo, COD] puede darte la nueva perspectiva de que necesitas volver a abordar el problema.
¿Has vocalizado el problema?
En serio otra vez. A veces, explicar el problema en voz alta nos lleva a nuestras propias respuestas. Hable con el pingüino (peluche) porque sus compañeros de trabajo no están escuchando. Si está interesado en esto como una herramienta de depuración seria (y lo recomiendo si no ha encontrado el problema por ahora), también puede leer The Psychology of Computer Programming .
die
declaraciones de los die
y otros errores de tiempo de ejecución y de tiempo de ejecución fatales se imprimen en STDERR
, lo cual puede ser difícil de encontrar y se puede combinar con los mensajes de otras páginas web de su sitio. Mientras depura su secuencia de comandos, es una buena idea obtener los mensajes de error fatales para mostrar en su navegador de alguna manera.
Una forma de hacerlo es llamar
use CGI::Carp qw(fatalsToBrowser);
en la parte superior de tu script Esa llamada instalará un controlador $SIG{__DIE__}
(ver perlvar ) mostrará los errores fatales en su navegador, anteponiéndolos con un encabezado válido si es necesario. Otro truco de depuración de CGI que utilicé antes de haber oído hablar de CGI::Carp
fue utilizar eval
con las instalaciones DATA
y __END__
en el script para capturar errores en tiempo de compilación:
#!/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
Esta técnica más detallada tiene una ligera ventaja sobre CGI::Carp
porque atrapará más errores en tiempo de compilación.
Actualización: nunca lo he usado, pero parece que CGI::Debug
, como sugirió Mikael S, también es una herramienta muy útil y configurable para este propósito.