Dav*_*d B 2 perl pipe filehandle
我试图bp_genbank2gff3.pl
从另一个获取genbank作为其参数的perl脚本运行(bioperl包).
这不起作用(不生成输出文件):
my $command = "bp_genbank2gff3.pl -y -o /tmp $ARGV[0]";
open( my $command_out, "-|", $command );
close $command_out;
Run Code Online (Sandbox Code Playgroud)
但确实如此
open( my $command_out, "-|", $command );
sleep 3; # why do I need to sleep?
close $command_out;
Run Code Online (Sandbox Code Playgroud)
为什么?
我认为close
在命令完成之前应该阻止:
关闭任何管道文件句柄会导致父进程等待子进程完成...(请参阅http://perldoc.perl.org/functions/open.html).
编辑
我在最后一行添加了这个:
say "ret=$ret, \$?=$?, \$!=$!";
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,打印输出是:
ret=, $?=13, $!=
Run Code Online (Sandbox Code Playgroud)
(这close
两种情况都意味着失败,对吧?)
$? = 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;
Run Code Online (Sandbox Code Playgroud)
更新:如果您真的不关心命令的输出,可以SIGPIPE
通过将输出重定向到/dev/null
以下内容来避免问题:
open my $command_out, "-|", "$command > /dev/null";
close $command_out; # succeeds, no SIGPIPE
Run Code Online (Sandbox Code Playgroud)
当然,如果你要忽略输出那么麻烦,你也可以使用system
.
附加信息:正如OP所说,关闭管道文件句柄会导致父母等待孩子完成(通过使用waitpid
或类似的东西).但在它开始等待之前,它会关闭管道的末端.在这种情况下,该结尾是子进程正在将其标准输出写入的管道的读取结束.下一次孩子尝试将某些内容写入标准输出时,操作系统会检测到该管道的读取端已关闭并向SIGPIPE
子进程发送一个进程,将其终止并快速让close
父进程中的语句完成.