Perl双向管道IPC,如何避免输出缓冲



我正在尝试与一个交互式进程进行通信。我希望我的perl脚本成为用户和进程之间的"moddle man"。该过程将文本放入stdout,提示用户输入命令,将更多文本放入stdout,提示用户输出命令。。。。。。。提供了一个原始图形:

User <----STDOUT---- interface.pl <-----STDOUT--- Process
User -----STDIN----> interface.pl ------STDIN---> Process
User <----STDOUT---- interface.pl <-----STDOUT--- Process
User -----STDIN----> interface.pl ------STDIN---> Process
User <----STDOUT---- interface.pl <-----STDOUT--- Process
User -----STDIN----> interface.pl ------STDIN---> Process

以下模拟了我要做的事情:

#!/usr/bin/perl
use strict;
use warnings;
use FileHandle;
use IPC::Open2;
my  $pid = open2( *READER, *WRITER, "cat -n" );
WRITER->autoflush(); # default here, actually
my $got = "";
my $input = " ";
while ($input ne "") {
chomp($input = <STDIN>);
print WRITER "$input n";
$got = <READER>;
print $got;
}

DUe输出缓冲上述例子不起作用。无论输入了什么文本,或者按下了多少输入键,程序都会一直保持不变。解决方法是发布:

my  $pid = open2( *READER, *WRITER, "cat -un" );

注意"cat-un",而不仅仅是"cat-n"-u关闭cat上的输出缓冲。当输出缓冲关闭时,这会起作用。我试图与最有可能的缓冲区交互的过程输出,因为我面临着与"cat-n"相同的问题。不幸的是,我无法关闭与我通信的进程的输出缓冲,那么我该如何处理这个问题呢?

UPDATE1(使用ptty):

#!/usr/bin/perl
use strict;
use warnings;
use IO::Pty;
use IPC::Open2;
my $reader = new IO::Pty;
my $writer = new IO::Pty;
my  $pid = open2( $reader, $writer, "cat -n" );
my $got = "";
my $input = " ";
$writer->autoflush(1);
while ($input ne "") {
chomp($input = <STDIN>);
$writer->print("$input n");
$got = $reader->getline;
print $got;
}

~

有三种缓冲:

  1. 块缓冲:输出被放入固定大小的缓冲区。缓冲区已满时会进行刷新。您将看到输出以块的形式出现
  2. 行缓冲:输出被放入一个固定大小的缓冲区。当一个新行被添加到缓冲区时,当缓冲区变满时,缓冲区将被刷新
  3. 无缓冲:输出直接传递给操作系统

在Perl中,缓冲的工作方式如下:

  • 默认情况下,文件句柄是缓冲的。一个例外:STDERR默认情况下不缓冲
  • 使用块缓冲。一个例外:STDOUT是行缓冲的,当且仅当它连接到一个端子时
  • 从STDIN读取会刷新STDOUT的缓冲区
  • 直到最近,Perl还使用4KB缓冲区。现在,默认值是8KB,但在构建Perl时可以更改

前两个在所有应用程序中都是令人惊讶的标准。这意味着:

  • User -------> interface.pl

    用户是一个人。尽管这是一个非常缓慢的数据来源,但他并没有缓冲正常

  • interface.pl ----> Process

    interface.pl的输出是块缓冲的不良

    通过在interface.pl中添加以下内容修复:

    use IO::Handle qw( );
    WRITER->autoflush(1);
    
  • Process ----> interface.pl

    进程的输出是块缓冲的不良

    通过在Process中添加以下内容修复:

    use IO::Handle qw( );
    STDOUT->autoflush(1);
    

    现在,你可能会告诉我你不能改变流程。如果是这样的话,你就有三个选择:

    • 使用工具提供的命令行或配置选项来更改其缓冲行为。我不知道有什么工具可以提供这样的选择
    • 通过使用伪tty而不是管道来欺骗孩子使用行缓冲而不是块缓冲
    • 退出

  • interface.pl -------> User

    interface.pl的输出是行缓冲的正常(对吗?)

最新更新