perl中管道文件句柄的问题

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两种情况都意味着失败,对吧?)

mob*_*mob 5

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

通过sleep3秒钟,你让你的程序在操作系统杀死之前运行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父进程中的语句完成.

  • @David B - 当一个进程(在这个例子中你的孩子)写入一个读者(你的父)已经关闭的管道时,你得到一个`SIGPIPE`.我在父文件中编写`<$ command_out>`,您将保持管道的读取端打开,直到管道的写入结束. (2认同)
  • 它会读出孩子想要写的东西.这有所不同.您正在将水转向花园软管并通过不读取打开管道的程序的输出来堵塞软管的尖端.如果您不想要程序的输出,请使用`system`. (2认同)