我正在尝试从另一个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
在两种情况下都失败了,对吗?)
答案 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在这种情况下可能更好......