2008-09-20 15 views
56

¿Es posible ejecutar un proceso externo desde Perl, capturar su stderr, stdout Y el código de salida del proceso?¿Cómo captura stderr, stdout y el código de salida de una sola vez en Perl?

Parece que puedo hacer combinaciones de estos, p. Ej. utilice los trazos inversos para obtener stdout, IPC :: Open3 para capturar salidas, y system() para obtener los códigos de salida.

¿Cómo se capturan stderr, stdout y el código de salida a la vez?

Respuesta

26

Si vuelve a leer la documentación de IPC :: Open3, verá una nota que debe llamar al waitpid para obtener el proceso secundario. Una vez que haga esto, el estado debería estar disponible en $?. El valor de salida es $? >> 8. Ver $? in perldoc perlvar.

+1

No sé por qué los enlaces están jodidos. Todo se ve bien en editar/vista previa. –

+2

He enviado perl5porters un parche a IPC :: Open2 y :: Open3 para mostrar las cosas de waitpid en el SINOPSIS de esos módulos. :) –

7

Hay tres formas básicas de ejecución de comandos externos:

system $cmd;  # using system() 
$output = `$cmd`;  # using backticks (``) 
open (PIPE, "cmd |"); # using open() 

Con system(), tanto STDOUT y STDERR seguirá el mismo lugar que el guión de STDOUT y STDERR, menos que el comando system() las redirecciona. Backticks y open() solo leen el STDOUT de su comando.

También puede llamar a algo como lo siguiente con abrir para redireccionar STDOUT y STDERR.

open(PIPE, "cmd 2>&1 |"); 

El código de retorno siempre se almacena en $? como señaló @Michael Carman.

+0

No se olvide 'qx //' –

41

(actualización: Me ha actualizado la API para IO :: CaptureOutput a hacer esto aún más fácil.)

Hay varias maneras de hacer esto. Aquí hay una opción, usando el módulo IO::CaptureOutput:

use IO::CaptureOutput qw/capture_exec/; 

my ($stdout, $stderr, $success, $exit_code) = capture_exec(@cmd); 

Ésta es la función capture_exec(), pero IO :: CaptureOutput también tiene una función de captura más general() que se puede utilizar para capturar cualquier salida Perl o salida de programas externos Entonces, si algún módulo de Perl utiliza algún programa externo, aún obtendrá la salida.

También significa que solo necesita recordar un enfoque único para capturar STDOUT y STDERR (o fusionarlos) en lugar de usar IPC :: Open3 para programas externos y otros módulos para capturar la salida de Perl.

+12

Parece que [Capture :: Tiny] (http://search.cpan.org/dist/Capture-Tiny/) es más nuevo y mejor: 'Este módulo fue, inspirado por IO :: CaptureOutput, que proporciona una funcionalidad similar sin la capacidad de salida de salida y con código y API más complicados. IO :: CaptureOutput no maneja capas o la mayoría de los casos inusuales descritos en la sección "Limitaciones" y ya no lo recomiendo. - http://search.cpan.org/~dagolden/Capture-Tiny-0.18/lib/Capture/Tiny.pm#SEE_ALSO –

+0

Consulte http://stackoverflow.com/a/8781408/110126 para ver un ejemplo del uso de Capture :: Tiny. – buzz3791

+5

@PhilipDurbin es gracioso que el autor de esta respuesta sea el mismo tipo que el autor de Capture :: Tiny. :) – simbabque

14

Si no desea que el contenido de STDERR, entonces la orden de captura() del módulo de IPC::System::Simple es casi exactamente lo que está buscando:

use IPC::System::Simple qw(capture system $EXITVAL); 

    my $output = capture($cmd, @args); 

    my $exit_value = $EXITVAL; 

Puede usar captura() con un argumento único invocar el shell, o múltiples argumentos para evitar de manera confiable el shell. También hay capturex() que nunca llama al shell, incluso con un solo argumento.

A diferencia de los comandos integrados de sistema y retroceso de Perl, IPC :: System :: Simple devuelve el valor de salida completo de 32 bits en Windows. También arroja una excepción detallada si el comando no puede iniciarse, muere a una señal o devuelve un valor de salida inesperado.Esto significa que para muchos programas, en lugar de comprobar la salida valora a sí mismo, se puede confiar en IPC :: System :: Simple para hacer el trabajo duro para usted:

use IPC::System::Simple qw(system capture $EXIT_ANY); 

system([0,1], "frobincate", @files);  # Must return exitval 0 or 1 

my @lines = capture($EXIT_ANY, "baznicate", @files); # Any exitval is OK. 

foreach my $record (@lines) { 
    system([0, 32], "barnicate", $record); # Must return exitval 0 or 32 
} 

IPC :: System :: Simple es pura Perl, no tiene dependencias, y funciona tanto en sistemas Unix como Windows. Desafortunadamente, no proporciona una forma de capturar STDERR, por lo que puede no ser adecuado para todas sus necesidades.

IPC::Run3 proporciona una interfaz limpia y fácil para volver a conectar las tres manejadoras de archivos comunes, pero desafortunadamente no comprueba si el comando fue exitoso, por lo que deberá inspeccionar $? manualmente, lo cual no es para nada divertido. ¿Proporciona una interfaz pública para inspeccionar $? es algo que está en mi to-do list para IPC :: System :: Simple, desde inspeccionar $? en una plataforma multiplataforma no es una tarea que desearía a nadie.

Hay otros módulos en el espacio de nombres IPC:: que también pueden proporcionarle ayuda. YMMV.

Todo lo mejor,

Paul

+0

Técnicamente, tiene una dependencia, pero solo en Windows, donde requiere Win32 :: Process. Si bien esto no se encuentra en Core Perl, se incluye con las distribuciones ActiveState Perl y Strawberry Perl. – xdg

+0

+1, uso IPC :: System :: Simple todo el tiempo. – Ether

0

Si usted está poniendo muy complicado, es posible que desee probar Expect.pm. Pero eso probablemente sea excesivo si no necesita administrar el envío de entradas al proceso también.

0

Encontré IPC:run3 para ser muy útil. Puede reenviar todas las tuberías secundarias a un globo o a una variable; ¡muy facilmente! Y el código de salida se almacenará en $ ?.

A continuación es cómo agarré stderr que sabía que sería un número. El cmd dio como resultado transformaciones informáticas a stdout (que canalicé a un archivo en args usando>) e informó cuántas transformaciones a STDERR.

use IPC::Run3 

my $number; 
my $run = run3("cmd arg1 arg2 >output_file",\undef, \undef, \$number); 
die "Command failed: $!" unless ($run && $? == 0); 
Cuestiones relacionadas