【问题标题】:Perl file handle with scalar target and IPC::Open3带有标量目标和 IPC::Open3 的 Perl 文件句柄
【发布时间】:2016-08-16 19:07:52
【问题描述】:

我的任务是从 perl 启动一个程序,并保持对启动任务的控制流,以及在标量变量中捕获程序的输出。该脚本应仅使用 perl 基础包中提供的 perl 模块。

我的第一个方法是

use POSIX;
use IPC::Open3;
use strict;

my ($invar, $outvar, $errvar, $in, $out, $err, $pid, $pidr);
open($in, "<",\$invar);
open($out, ">",\$outvar);
open($err, ">",\$errvar);
my $cmd = "sleep 5; echo Test; sleep 5; echo Test; sleep 5;";
$pid = open3($in, $out, $err, $cmd);
my $num = 0;
for($pidr = $pid; $pidr >= 0;)
{
  sleep(1) if ($pidr = waitpid($pid, WNOHANG)) >= 0;
  print "$num: $outvar" if $outvar;
  ++$num;
}
close($in);
close($out);
close($err);

当开始时没有任何反应。启动程序的输出不会进入$outvar。为了测试我的基本想法是否失败,我尝试了这个:

my $outvar = "";
my $out;
open($out, ">", \$outvar);
print $out "Test\n";
print "--1--\n$outvar";
print $out "Test2\n";
print "--2--\n$outvar";

按预期正确输出:

--1--
Test
--2--
Test
Test2

问题是:为什么上面的程序不能作为测试示例输出应该在$outvar的文本?

一个可行的解决方案要复杂得多(当您添加此示例中遗漏的所有安全检查时):

use POSIX;
use IPC::Open3;
use strict;

my ($invar, $outvar, $errvar, $in, $out, $err, $pid, $pidr);
open($in, "<",\$invar);
open($out, ">",\$outvar);
open($err, ">",\$errvar);
my $cmd = "sleep 5; echo Test; sleep 5; echo Test; sleep 5;";
$pid = open3($in, $out, $err, $cmd);
my $num = 0;
for($pidr = $pid; $pidr >= 0;)
{
  sleep(1) if ($pidr = waitpid($pid, WNOHANG)) >= 0;
  my $obits; vec($obits, fileno($out), 1) = 1;
  if(select($obits, undef, undef, 0) > 0)
  {
    my $buffer;
    sysread($out, $buffer, 10240);
    print "$num: $buffer" if $buffer;
  }
  ++$num;
}
close($in);
close($out);
close($err);

它可以正确打印(第一个也应该以类似的方式打印):

5: Test
10: Test

对于示例,我删除了大部分错误处理和 STDERR 的类似代码。

【问题讨论】:

  • 顺便说一下,它被称为“文件句柄”(因为它持有系统资源),而不是“文件处理程序”(因为它不处理任何东西,因为它不是代码)。

标签: perl asynchronous filehandle


【解决方案1】:

open($out, "&gt;", \$outvar); 不创建系统文件句柄。你会注意到fileno($out)-1。一个进程不能写入另一个进程的内存,更不用说操纵它的变量,所以整个概念是有缺陷的。使用IPC::Run3IPC::Run;他们有效地为您实现了select 循环。 open3 对于大多数应用程序来说太低级了。


第二个 sn-p 有效,因为 open3 的行为就像 $in$out$err 是未打开的句柄。你可能已经完成了

$in = gensym();
$out = gensym();
$err = gensym();

但同样,您应该使用 IPC::Run3 或 IPC::Run。您的手卷版本存在一些错误,包括可能导致死锁的错误。

【讨论】:

  • 你的文字很难看懂,但是看了几遍就明白你的意思了.
  • Re "你的文字很难理解",如果有什么不清楚的地方,请告诉我,我会进一步解释。我用简单的句子说话,所以我看不出问题出在哪里。 /// 我知道 IPC::Run3 对你没有用,但我把它包括在内是因为你不是这个答案的唯一读者,而且它比 IPC::Run 更简单。 IPC::Run,otoh,确实满足您的需求。 (使用start+pump+finish 而不是run。)
  • 我稍微修改了你的文字。可以跨进程直接写入变量。我还尝试测试线程:共享文本变量。只是在这种情况下不支持。
  • @Dirk Stöcker,不要用声称根本不支持它来解释为什么一种方法不可能的解释替换某人的答案。我恢复了你的编辑。写入系统文件句柄不能直接导致变量在另一个进程中被更改。
  • 我什么时候应该编辑帖子?:澄清帖子的含义(不改变含义) - 如果您认为说明线程间/线程间数据存储的概念有缺陷,那么可以.我只是觉得那是你的一个糟糕的措辞。如果您认为无法访问其他线程或其他进程内存,那么您似乎还需要学习一点。
猜你喜欢
  • 2014-07-09
  • 2012-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-16
  • 1970-01-01
  • 2017-02-18
  • 2011-09-04
相关资源
最近更新 更多