我浏览了open3的文档,这是我无法理解的部分:
如果您尝试从孩子的 stdout writer 和他们的 stderr writer 读取,您将遇到阻塞问题,这意味着您将要使用 select() 或 IO::Select,这意味着您最好使用 sysread( ) 而不是 readline() 用于普通的东西。
这是非常危险的,因为您可能会永远阻塞。它假设它将与 bc 之类的东西交谈,既向它写入数据,又从中读取数据。这大概是安全的,因为您“知道”像 bc 这样的命令将一次读取一行并一次输出一行。然而,像 sort 这样的程序首先读取整个输入流,很容易导致死锁。
所以我尝试了,open3
希望能更好地了解它。这是第一次尝试:
sub hung_execute {
my($cmd) = @_;
print "[COMMAND]: $cmd\n";
my $pid = open3(my $in, my $out, my $err = gensym(), $cmd);
print "[PID]: $pid\n";
waitpid($pid, 0);
if(<$err>) {
print "[ERROR] : $_" while(<$err>);
die;
}
print "[OUTPUT]: $_" while (<$out>);
}
有趣的是,我必须$err
在这里初始化。
无论如何,当我execute("sort $some_file");
给出$some_file
一个包含超过 4096 个字符(我的机器的限制)的文本文件时,它就会挂起。
然后我查看了这个常见问题解答,下面是我的新版本执行:
sub good_execute {
my($cmd) = @_;
print "[COMMAND]: $cmd\n";
my $in = gensym();
#---------------------------------------------------
# using $in, $out doesn't work. it expects a glob?
local *OUT = IO::File->new_tmpfile;
local *ERR = IO::File->new_tmpfile;
my $pid = open3($in, ">&OUT", ">&ERR", $cmd);
print "[PID]: $pid\n";
waitpid($pid, 0);
seek $_, 0, 0 for \*OUT, \*ERR;
if(<ERR>) {
print "[ERROR] : $_" while(<ERR>);
die;
}
print "[OUTPUT]: $_" while (<OUT>);
}
该sort
命令现在执行得很好,但我不知道为什么。
[更新]在阅读了@tchrist 的回答后,我阅读IO::Select
了,并且经过更多的谷歌搜索,提出了这个版本execute
:
sub good_execute {
my($cmd) = @_;
print "[COMMAND]: $cmd\n";
my $pid = open3(my $in, my $out, my $err = gensym(), $cmd);
print "[PID]: $pid\n";
my $sel = new IO::Select;
$sel->add($out, $err);
while(my @fhs = $sel->can_read) {
foreach my $fh (@fhs) {
my $line = <$fh>;
unless(defined $line) {
$sel->remove($fh);
next;
}
if($fh == $out) {
print "[OUTPUT]: $line";
}elsif($fh == $err) {
print "[ERROR] : $line";
}else{
die "[ERROR]: This should never execute!";
}
}
}
waitpid($pid, 0);
}
这工作正常,现在一些事情变得更清楚了。但整体画面还是有些朦胧。
所以我的问题是:
- 有什么问题
hung_execute
? - 我猜
good_execute
是因为>&
open3 调用中的。但为什么以及如何? - 此外,
good_execute
当我使用词法变量(my $out
而不是OUT
)作为文件句柄时,它也不起作用。它给出了这个错误:open3: open(GLOB(0x610920), >&main::OUT) failed: Invalid argument
。为什么这样? - 似乎只有一个文件句柄可以在给定时间写入,如果我丢弃持有资源的句柄,其他句柄继续等待。我曾经认为STDERR和STDOUT是独立的流,不共享任何资源。我想我的理解在这里有点缺陷。也请给我一些指示。