¿Cómo puedo ejecutar un comando del sistema y morir si algo está escrito en STDERR?

StackOverflow https://stackoverflow.com/questions/4377967

  •  09-10-2019
  •  | 
  •  

Pregunta

Estoy escribiendo un script en Perl que utiliza un script externo. El script externo debe ejecutar desde un directorio específico así que encontré la siguiente utilidad:

use IPC::System::Simple qw(capture);

my @args = ('external script path...', 'arg1', ...);
my $out = capture( [0], "cd $dir ; @args" );

A veces el script externo escribe cosas para STDERR pero todavía devuelve 0. I deseo de capturar estos momentos y confess (o die). Dado que no controlo el valor de retorno del script externo, pensé que tal vez podría capturar su STDERR así que voy a tener algo como esto:

my ($out, $err) = cool_capture( [0], "cd $dir ; @args" );
say "Output was: $out";
if ($err) {
 die "Error: this was written to STDERR: $err";
}

¿Qué puedo hacer?

¿Fue útil?

Solución

Este es cubierto en el Perl FAQ.

Suponiendo test_app es un programa que salidas de una línea a la salida estándar y una línea a stderr:

use IPC::Open3;
use Symbol 'gensym';

my($wtr, $rdr, $err);
$err = gensym;

my $pid = open3($wtr, $rdr, $err, 'test_app');

waitpid($pid, 0);
my $status = $? >> 8;

my $stdout = <$rdr>;
my $stderr = <$err>;

print "out output: $stdout\n";
print "err output: $stderr\n";

print "Exit code: $status\n";

EDIT: Por la solicitud actualizada para incluir la captura el código de salida. También se podía haber pedido perldoc IPC::Open3 que dice

waitpid( $pid, 0 );
my $child_exit_status = $? >> 8;

Y que debería leer todos modos por sus precauciones y advertencias.

Otros consejos

Si la salida significativa se están escribiendo en la salida estándar y / o stderr o usted es la lectura y la escritura para el proceso. Tienes que ser mucho más cuidadoso con su I / O manipulación para evitar diversos problemas de bloqueo.

my ($wtr, $rdr, $err) ;

my $pid = IPC::Open3::open3($wtr, $rdr, $err, @_);

close($wtr);

my $stdout = '';
my $stderr = '';

my $s = IO::Select->new;

$s->add($rdr) if $rdr;
$s->add($err) if $err;

while (my @ready = $s->can_read) {

    foreach my $ioh (@ready) {

        my $bytes_read = sysread($ioh, my $chunk = '', 1024);

        die "read error: $!" unless $bytes_read >= 0;

        if ($bytes_read) {
           ($ioh eq $rdr? $stdout: $stderr) .= $chunk;
        }
    else {
            $s->remove($ioh);
        }
    }
} 

my $pid1;
for (;;) {

    last if kill(0, $pid);

    $pid1 = wait();
    #
    # Wait until we see the process or -1 (no active processes);
    #
    last if ($pid1 == $pid || $pid1 <= 0);
}

Acabado de leer, antes de apagar el proceso. Si va a escribir a la entrada estándar del proceso, usted también necesita agregar $ wtr y syswrite de lo anterior seleccione bucle.

Editar

Justificación:

Lo anterior es algo excesivo para los casos simples. Este manejo avanzado de entrada y salida entra en juego cuando es muy probable que pasar más de unos pocos K de los datos.

Usted no lo necesitan si se ejecuta un comando 'df', por ejemplo.

Sin embargo, es cuando buffers del sistema para cualquiera de stdin, stdout o stderr de llenado hasta que el bloqueo se convierte en probable y las cosas pueden involucrarse más.

Si el proceso hijo llena los buffers stderr y / o stdout, es probable que va a bloquear y esperar a que para eliminarlas. Pero si usted está esperando a que termine el proceso antes de leer desde la salida estándar o stderr; eso es un callejón sin salida. Es probable que vea que la llamada al sistema nunca termina y el proceso hijo nunca se completa.

Hay una posibilidad similar de punto muerto si la entrada estándar se están escribiendo, pero el proceso hijo es capaz de consumir la entrada. Esto es particularmente probable en una situación 'tubería' en el que el proceso hijo está consumiendo de entrada y escribiendo en la salida estándar.

El bucle de selección es acerca de la eliminación progresiva de las memorias intermedias para evitar el bloqueo. Tanto stdout y stderr son monitoreados simultáneamente.

Si va a escribir a la entrada estándar y la lectura de salida estándar (un tubo), tendrá que mantener stdout y stderr clara y sólo se escribe en la entrada estándar cuando está listo para recibir la entrada.

Simplemente esperar a que el proceso hasta el final, entonces la lectura de stdout / stderr probablemente funcione el 90% del tiempo. Esta respuesta es sólo para darle lugar a donde ir si las cosas se complican y los procesos comienzan a bloquear o entrar en punto muerto.

Edit2

En cuanto a cuál utilizar, yo diría que empezar de forma sencilla, dura prueba.

Ir con el enfoque de Sorpigal, pero tratan de prueba de esfuerzo con mayores volúmenes de datos y bajo cargas más difíciles y condicionales que alguna vez había esperar en un sistema vivo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top