我可以从 perl 中的进程捕获 STDOUT 写入事件吗?

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

我需要(想要?)使用 Minion 队列从 Web 应用程序生成一个缓慢的进程。

过程 - GLPK 求解器 - 可以 运行 很长时间但会生成进度输出。

我想在输出发生时捕获它并将其写入某个地方(数据库?日志文件?)以便它可以作为网络应用程序内的状态更新回放给用户。

这可能吗?我不知道(因此没有代码)。

我正在探索 Capture::Tiny - 它的简单性很好,但我不知道它是否可以在写入时跟踪写入事件。

一种基本方法是使用管道打开,您可以在其中 open 一个管道到一个被分叉的进程。然后 child 中的 STDOUT 通过管道传输到 parent 中的文件句柄,或者 parent 通过管道传输到它的 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: $!";

这样 parent 在发出时就接收到 child 的 STDOUT

错误检查还可以做更多的事情,请参阅手册页,close, and $? in perlvar. Also, install a handler for SIGPIPE, see perlipc and %SIG in perlvar

有些模块可以更轻松地 运行 和管理外部命令,尤其是检查错误。但是,Capture::Tiny and IPC::Run3 使用文件传输外部程序的流。

另一方面,IPC::Run 给你更多的控制和权力。

执行代码"... each time some data is read from the child"使用回调

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] };

一旦你使用 IPC::Run 就可以有更多的可能,包括更好的错误审讯、为进程设置伪 tty 等。例如,使用 >pty> 而不是 >设置一个 terminal-like 环境,以便 运行 的外部程序可以返回行缓冲并提供更及时的输出。如果对如何管理流程的需求变得更加复杂,那么使用该模块可以更轻松地工作。

感谢 ikegami 的评论,包括演示 @cmd


为了证明 parent 在发出时接收到 child 的 STDOUT 使用延迟发出输出的命令.例如,使用

而不是上面的 ls -l
my @cmd = (
    'perl', 
    '-le', 
    'STDOUT->autoflush(1); for (qw( abc def ghi )) { print; sleep 1; }'
);

这个 Perl one-liner 以一秒的间隔打印单词,这就是它们在屏幕上结束的方式。