我有一个Perl脚本,它使用外部工具(cleartool)来收集有关文件列表的信息。我想使用IPC来避免为每个文件生成一个新进程:
use IPC::Open2;
my ($cin, $cout);
my $child = open2($cout, $cin, 'cleartool');
返回单行的命令运行良好。 e.g。
print $cin "describe -short $file\n";
my $description = <$cout>;
返回多行的命令让我处于死路,以便如何使用整个响应而不会被阻塞读取挂起:
print $cin "lshistory $file\n";
# read and process $cout...
我尝试通过fcntl
设置非阻塞读取的文件句柄:
use Fcntl;
my $flags = '';
fcntl($cout, F_GETFL, $flags);
$flags |= O_NONBLOCK;
fcntl($cout, F_SETFL, $flags);
但是Fcntl的消息是“你的供应商还没有定义Fcntl宏F_GETFL。”
我尝试使用IO :: Handle设置$cout->blocking(0)
但是失败了(它返回undef
并将$!
设置为“未知错误”。)
在尝试阅读之前,我尝试使用select
来确定是否有可用数据:
my $rfd = '';
vec($rfd, fileno($cout), 1) = 1;
while (select($rfd, undef, undef, 0) >= 0) {
my $n = read($cout, $buffer, 1024);
print "Read $n bytes\n";
# do something with $buffer...
}
但是没有读过任何东西就挂了。有谁知道如何使这项工作(在Windows上)?
答案 0 :(得分:5)
select
only works on sockets。看起来IPC :: OpenX使用普通的文件句柄,因此您无法将select
与其创建的句柄一起使用。
如果您不需要select提供的活动的超时/检测,您可以将句柄设置为非阻塞,并按正常方式读取或写入。
如果您需要更细致的控制,IPC::Run可能适合您。
您还可以查看创建socketpair
并将这些句柄与子进程一起使用。较新的perls(5.8及更高版本)支持使用TCP套接字在Windows上进行socketpair
仿真。
如果您尝试克隆STDOUT
和STDERR
以获取在没有控制台的情况下运行的程序(即使用wperl而不是perl启动),您将无法获取数据STDIO。
在实践中,这对我来说在几个项目上都是一个巨大的痛苦。我发现最有效的方法是编写子进程以通过TCP连接到父服务器。如果您不控制子流程,请查看IPC::Run
或socketpair
。
答案 1 :(得分:1)
另一个问题是使用sysread
具有较大或不太可能的缓冲区大小。
print $cin "$command_for_cleartool\n";
my ($description, $maxlen, $buffer) = ("", 65336);
while (my $n = sysread $cout, $buffer, $maxlen) {
$description .= $buffer;
last if $n < $maxlen;
}
... do something with $description ...
如果正好有0字节的输入等待读取, sysread
将挂起。因此,如果cleartool产生65336字节的正好倍数,则上面的代码将挂起。如果您知道程序输出大小的上限,则可以将该值用于上面的$maxlen
。否则,你可以选择一个大而且不太可能的数字并祈祷......
答案 2 :(得分:0)
Windows上的非阻塞IO很难。在这种情况下,您可以将cleartool的输出发送到常规文件,并在每次从文件中读取时使用seek
重置文件上的eof标志:
my($cin, $cout);
my $somefile = "some/file";
open($cin, "| cleartool > $somefile");
open($cout, '<', $somefile);
...
print $cin "$command_for_cleartool\n";
# if necessary, wait until cleartool finishes with new output
seek $cout, 0, 1; # clear eof condition from previous read
my @cleartool_output = <$cout>; # capture recent output
... process output ...
如果cleartool缓冲其输出,这可能不会那么好。