Can I capture STDOUT write events from a process in perl?

点点圈 提交于 2020-02-24 10:44:29

问题


I need (would like?) to spawn a slow process from a web app using a Minion queue.

The process - a GLPK solver - can run for a long time but generates progress output.

I'd like to capture that output as it happens and write it to somewhere (database? log file?) so that it can be played back to the user as a status update inside the web app.

Is that possible? I have no idea (hence no code).

I was exploring Capture::Tiny - the simplicity of it is nice but I can't tell if it can track write events upon writing.


回答1:


A basic way is to use pipe open, where you open a pipe to a process that gets forked. Then the STDOUT from the child is piped to the filehandle in the parent, or the parent pipes to its STDIN.

use warnings;
use strict;

my @cmd = qw(ls -l .);  # your command

my $pid = open(my $fh, '-|', @cmd)   // die "Can't open pipe from @cmd: $!";

while (<$fh>) {
    print;
}

close $fh or die "Error closing pipe from @cmd: $!";

This way the parent receives child's STDOUT right as it is emitted.

There is a bit more that you can do with error checking, see the man page, close, and $? in perlvar. Also, install a handler for SIGPIPE, see perlipc and %SIG in perlvar.

There are modules that make it much easier to run external commands and, in particular, check errors. However, Capture::Tiny and IPC::Run3 use files to transfer the external program's streams.

On the other hand, the IPC::Run gives you far more control and power.

To have code executed "... each time some data is read from the child" use a callback

use warnings;
use strict;

use IPC::Run qw(run);

my @cmd = ( 
    'perl', 
    '-le', 
    'STDOUT->autoflush(1); for (qw( abc def ghi )) { print; sleep 1; }'
);

run \@cmd, '>', sub { print $_[0] };

Once you use IPC::Run a whole lot more is possible, including much better error interrogation, setting up pseudo tty for the process, etc. If demands on how to manage the process grow more complex then work will be easier with the module.

Thanks to ikegami for comments, including the demo @cmd.


To demonstrate that the parent receives child's STDOUT as it is emitted use a command that emits output with delays. For example, instead of ls -l above use

my @cmd = (
    'perl', 
    '-le', 
    'STDOUT->autoflush(1); for (qw( abc def ghi )) { print; sleep 1; }'
);

This Perl one-liner prints words one second apart, and that is how they wind up on screen.



来源:https://stackoverflow.com/questions/51701670/can-i-capture-stdout-write-events-from-a-process-in-perl

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!