假设我有这样一个 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/