2

我正在尝试bp_genbank2gff3.pl从另一个以 genbank 作为参数的 perl 脚本运行(bioperl 包)。

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

   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在这两种情况下都失败了,对吧?)

4

2 回答 2

5

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

通过sleeping 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父进程中的语句完成。

于 2010-08-13T15:50:55.823 回答
0

我不确定您要做什么,但在这种情况下系统可能会更好...

于 2010-08-13T13:57:47.187 回答