perl中管道文件句柄的问题

时间:2010-08-13 12:47:02

标签: perl pipe filehandle

我正在尝试从另一个perl脚本运行bp_genbank2gff3.pl(bioperl包) 得到一个genbank作为其论点。

这不起作用(不生成输出文件):

   my $command = "bp_genbank2gff3.pl -y -o /tmp $ARGV[0]";

   open( my $command_out, "-|", $command );
   close $command_out;

但是这样做

   open( my $command_out, "-|", $command );
   sleep 3; # why do I need to sleep?
   close $command_out;

为什么?

我认为close应该阻止,直到命令完成:

  

关闭任何管道文件句柄会导致   父进程等待   孩子要完成......   (见http://perldoc.perl.org/functions/open.html)。

修改

我在最后一行添加了这个:

say "ret=$ret, \$?=$?, \$!=$!";

并且在两种情况下打印输出都是:

ret=, $?=13, $!=

(这意味着close在两种情况下都失败了,对吗?)

2 个答案:

答案 0 :(得分:5)

$? = 13表示您的子进程已被SIGPIPE信号终止。您的外部程序(bp_genbank2gff3.pl)尝试将一些输出写入您perl程序的管道。但perl程序关闭了管道的末尾,因此您的操作系统向外部程序发送了SIGPIPE

通过sleep 3秒钟,你让你的程序在操作系统杀死之前运行3秒钟,这样就可以让你的程序完成任务。请注意,管道的容量有限,因此如果您的父perl脚本没有从管道中读取,并且外部程序正在大量写入标准输出,则外部程序的写入操作最终会阻塞,您可能会你的外部程序并没有真正得到3秒的努力。

解决方法是从外部程序中读取输出,即使您只是要扔掉它。

open( my $command_out, "-|", $command );
my @ignore_me = <$command_out>;
close $command_out;

<小时/>

更新:如果您真的不关心命令的输出,可以通过将输出重定向到SIGPIPE来避免/dev/null问题:

open my $command_out, "-|", "$command > /dev/null";
close $command_out;     # succeeds, no SIGPIPE

当然,如果你要忽略输出那么麻烦,你也可以使用system


其他信息:正如OP所说,关闭管道文件句柄会导致父级等待孩子完成(使用waitpid或类似的东西)。但开始等待之前,它会关闭管道的末尾。在这种情况下,该结尾是子进程正在将其标准输出写入的管道的读取结束。下一次孩子尝试将某些内容写入标准输出时,操作系统会检测到该管道的读取端已关闭,并向子进程发送SIGPIPE,将其终止并快速释放close语句在父母完成。

答案 1 :(得分:0)

我不确定你要做什么,但system在这种情况下可能更好......