Creé un proceso hijo a través de IPC::Open2.
Necesito leer de la salida estándar de este proceso hijo línea por línea.
El problema es que, como la salida estándar del proceso hijo no está conectada a una terminal, está completamente almacenada en búfer y no puedo leer hasta que el proceso finaliza.

¿Cómo puedo vaciar la salida del proceso hijo sin modificar su código?


código de proceso secundario

while (<STDIN>) {
    print "Received : $_";
}

código de proceso principal:

use IPC::Open2;
use Symbol;

my $in = gensym();
my $out = gensym();

my $pid = open2($out, $in, './child_process');

while (<STDIN>) {
    print $in $_;
    my $line = <$out>;
    print "child said : $line";
}

Cuando ejecuto el código, se atasca esperando el resultado del proceso secundario .
Sin embargo, si lo ejecuto con bc el resultado es el que esperaba, creo que bc debe vaciar manualmente su salida

Nota:
En el proceso secundario, si agrego $| = 1 al principio o STDOUT->flush() después de imprimir, el proceso principal puede leerlo correctamente.
Sin embargo, este es un ejemplo y debo manejar programas que no vacíen manualmente su salida.

2
Yanis.F 5 feb. 2019 a las 18:53

2 respuestas

La mejor respuesta

Una forma es configurar un entorno similar a una terminal para el proceso, una pseudo-terminal (pty). Eso es difícil de hacer bien y depende mucho del sistema, pero IPC :: Run tiene eso capacidad lista para un uso fácil.

Aquí está el controlador, ejecútelo usando la función at para que no tenga una terminal de control (o ejecútelo a través de cron)

use warnings;
use strict;
use feature 'say';

use IPC::Run qw(run);

my @cmd = qw(./t_term.pl input arguments); 

run \@cmd, '>pty>', sub { say "out: @_" };

#run \@cmd, '>', sub { say "out: @_" }   # no pty

Con >pty> configura una pseudo-terminal para STDOUT del programa en @cmd (es una tubería con >); también vea <pty< y vea más acerca de la redirección. Se llama al sub {} anónimo cada vez que hay una salida del niño, por lo que se puede procesar a medida que avanza. También hay otras opciones para esto.

El programa que se llama (t_term.pl) solo prueba una terminal

use warnings;
use strict;
use feature 'say';

say "Is STDOUT filehandle attached to a terminal: ",
    ( (-t STDOUT) ? "yes" : "no" );
sleep 2;
say "bye from $$";

Los -t STDOUT (consulte operadores de prueba de archivos) es una forma adecuada de verificar para un terminal en este ejemplo. Para obtener más / otras formas, consulte esta publicación.

El resultado muestra que el programa llamado (t_term.pl) ve un terminal en su STDOUT, incluso cuando un controlador se ejecuta sin uno (usando at, o cuando se agota un {{X3 }}). Si >pty> se cambia a la redirección habitual con > (usando una tubería), entonces no hay terminal.

Si esto resuelve los problemas de almacenamiento en búfer depende claramente de ese programa, y si es suficiente engañarlo con una terminal.

Otra forma de solucionar el problema es usar unbuffer cuando sea posible, como en la respuesta de mob.

2
zdim 28 mar. 2019 a las 05:50

Desafortunadamente, Perl no tiene control sobre el comportamiento de almacenamiento en búfer de los programas que ejecuta. Algunos sistemas tienen una utilidad unbuffer que puede hacer esto. Si tiene acceso a esta herramienta, podría decir

my $pid = open2($out, $in, 'unbuffer ./child_process');

Hay una discusión aquí sobre las herramientas equivalentes para Windows , pero no podría decir si alguno de ellos es efectivo.

3
mob 5 feb. 2019 a las 16:03