perl - 在 perl 中使用 open3 写入之前从进程读取

标签 perl ipc

假设我有这样一个 C 程序:

...
puts("What is your name?");
scanf("%s", name);
puts("Thank You!");
...

该程序要求您输入姓名,接受输入,显示“谢谢!”消息并退出。

我想编写一个程序,它自动向我的 C 程序写入名称、接收输出并打印它。 Perl 中的以下程序运行良好:

use strict;
use IPC::Open3;

my $bin = './myniceprogram';

my $pid = open3(\*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR, "$bin")
    or die "open3() failed $!";

my $r;

print CHLD_IN "A"x10 . "\n";

$r = <CHLD_OUT>;
print "$r";

$r = <CHLD_OUT>;
print "$r";

waitpid $pid, 0;

它产生以下输出:

What is your name?
Thank You!

但是,我想在写入之前读取 C 程序的第一行(“你叫什么名字?”)。但是,如果我更改 Perl 程序中的读/写顺序,它就会挂起:

use strict;
use IPC::Open3;

my $bin = './myniceprogram';

my $pid = open3(\*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR, "$bin")
    or die "open3() failed $!";

my $r;

$r = <CHLD_OUT>;
print "$r";

print CHLD_IN "A"x10 . "\n";

$r = <CHLD_OUT>;
print "$r";

waitpid $pid, 0;

使用 strace 我可以看到它卡在读取调用上:

[myuser@myhost mydir]$ strace perl test2.pl
...
close(6)                                = 0
close(8)                                = 0
read(5,

但是为什么呢?

最佳答案

您正在遭受缓冲之苦。子程序可能使用正常约定,即 STDOUT 在连接到终端时是行缓冲的,否则是 block 缓冲的。

如果是这种情况,您可以使用伪 tty (ptty) 欺骗它,使其在输出换行时刷新其缓冲区。执行此操作的一个简单方法是执行 unbuffer ./myniceprogram 。 IPC::Run 还提供了使用伪 tty 的简单机制。 (只需使用 <pty<>pty> 而不是 <> 。)

但这不适用于“二进制数据”(未组织成以 LF 终止的行的数据)。对二进制数据使用行缓冲的程序应该被认为是有错误的。如果它不是交互式的,那么它应该始终使用 block 缓冲。但这是交互式的,因此它应该在适当的时间刷新其缓冲区或完全避免缓冲。解决此问题需要修改 myniceprogram .


同样,您可能希望禁用 CHLD_OUT 上的缓冲。有两种方法可以做到这一点:

打开句柄的自动刷新:

CHLD_OUT->autoflush( 1 );

每次写入后刷新句柄( print ):

CHLD_OUT->flush();

关于perl - 在 perl 中使用 open3 写入之前从进程读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73704264/

相关文章:

arrays - 如何循环进入数组哈希值的哈希值?

perl - 如何使用 Perl 获得频率序列的递减累积?

windows - Windows 中 Activestate Perl 中的 @INC 不正确

perl - 当该行包含额外的未转义双引号时,如何使用 Text::ParseWords::parse_line?

perl - 在 perl 中用散列编写交换键和值的最紧凑方法

c - UNIX 上的进程间通信

c - 在什么情况下我可能会失去对 malloc 分配的指针的访问权限?

delphi - 需要我的应用互相交流

java - 如何在 C# 和 Java 进程之间使用互斥体?

c - 从子进程检索 PID 和退出状态