我的理解是,关闭IO::Pipe
对象的句柄应该使用方法($fh->close
)而不是内置方法(close($fh)
)来完成。
前几天,我出于习惯在一个IO::Pipe
对象上搞砸并使用了内置的,该对象被打开到一个我预计会失败的命令。当$?
为零时,我很惊讶,并且没有触发我的错误检查。
我意识到了我的错误。如果我使用内置的,IO:Pipe
无法执行waitpid()
,也无法设置$?
。但令我惊讶的是,perl 似乎仍然关闭管道而没有通过核心设置$?
。
编写了一个小测试脚本来说明我的意思:
use 5.012;
use warnings;
use IO::Pipe;
say 'init pipes:';
pipes();
my $fh = IO::Pipe->reader(q(false));
say 'post open pipes:';
pipes();
say 'return: ' . $fh->close;
#say 'return: ' . close($fh);
say 'status: ' . $?;
say q();
say 'post close pipes:';
pipes();
sub pipes
{
for my $fd ( glob("/proc/self/fd/*") )
{
say readlink($fd) if -p $fd;
}
say q();
}
使用该方法时,它显示关闭后管道消失,并且$?
设置为我预期的那样:
init pipes:
post open pipes:
pipe:[992006]
return: 1
status: 256
post close pipes:
而且,当使用内置时,它似乎也会关闭管道,但不设置$?
:
init pipes:
post open pipes:
pipe:[952618]
return: 1
status: 0
post close pipes:
对我来说,内置导致管道关闭,但没有设置$?
似乎很奇怪.谁能帮助解释这种差异?
谢谢!
如果您查看 IO::Handle
的代码(其中 IO::Pipe::End
是子类),您将看到以下内容:
sub close {
@_ == 1 or croak 'usage: $io->close()';
my($io) = @_;
close($io);
}
看起来$fh->close
只是打电话给close $fh
.当然,我们不应该在幕后偷看。
我们可以看到IO::Pipe
执行close $fh
(幕后)后,它执行了等待:
package IO::Pipe::End;
our(@ISA);
@ISA = qw(IO::Handle);
sub close {
my $fh = shift;
my $r = $fh->SUPER::close(@_); # <-- This just calls a CORE::close
waitpid(${*$fh}{'io_pipe_pid'},0)
if(defined ${*$fh}{'io_pipe_pid'});
$r;
}
同样有趣的是,来自接近的Perldoc:
如果文件句柄来自管道打开,则当涉及的其他系统调用之一失败或其程序以非零状态退出时,close将返回false。如果唯一的问题是程序退出非零,$!将设置为 0 。
关闭管道还会等待在管道上执行的进程退出 - 如果您希望查看管道的输出
之后 - 并隐式放置该命令的退出状态值 成$?和 ${^CHILD_ERROR_NATIVE} 。
这回答了你的问题。
但令我惊讶的是,perl 似乎仍然在不设置 $? 通过核心关闭管道。
为什么会这样?它无法知道另一端的进程是一个孩子,更不用说程序应该等待的过程了。由于它没有理由打电话给waitpid
,$?
不会设置。
即使它愿意,因为我怀疑是否有一种方法可以在管道的另一端获取进程的 pid,因为实际上有可能在管道的另一端有多个进程。
IO::Pipe::close
仅在使用 IO::P ipe "打开进程"时调用waitpid
。
同样,close
仅在使用open
"打开进程"时调用waitpid
。
使用一种方法"打开"的进程不能被另一种方法关闭。
事实证明,我的困惑源于一个有缺陷的假设,即消失的管道与完整的进程终止相吻合。情况似乎并非如此,因为该过程仍然可用于wait()
。
> perl -MIO::Pipe -le 'my $io = IO::Pipe->reader(q(false)); close($io); print $?; print wait(); print $?'
0
8857
256