下面的代码:
#!/usr/bin/env perl
use 5.0360;
use warnings FATAL => 'all';
use autodie ':default';
use Devel::Confess 'color'; # not essential, but better error reporting
open my $view, "zcat a.big.file.vcf.gz|"; # zcat or bcftools
while (<$view>) {
next unless /^#CHROM\t/;
last;
}
close $view;
上面的代码崩溃并出现错误
Can't close(GLOB(0x55adfa96ebf8)) filehandle: '' at (eval 11)[/home/con/perl5/perlbrew/perls/perl-5.36.0/lib/5.36.0/Fatal.pm:1683] line 74
at (eval 11)[/home/con/perl5/perlbrew/perls/perl-5.36.0/lib/5.36.0/Fatal.pm:1683] line 74.
main::__ANON__[(eval 11)[/home/con/perl5/perlbrew/perls/perl-5.36.0/lib/5.36.0/Fatal.pm:1683]:86](GLOB(0x55adfa96ebf8)) called at mwe.pl line 13
Command exited with non-zero status 255
然而,如果我注解掉last
,代码运行时不会出现问题,但是,文件是 * 巨大的 *,这在运行时间上有很大的不同。
如果我删除close $view
,代码也可以工作,但close
是正确的做法。
如何同时使用last
和close $view
运行代码?
1条答案
按热度按时间bq3bfh9z1#
当您
last
阅读该进程(此处为zcat
)和close管道时,在该进程完成写入之前,该进程将获得一个SIGPIPE
如果在向管道另一端写入数据的进程完成写入之前关闭管道的读取端,则会导致写入程序收到SIGPIPE
因此,当
close
然后wait
s时,它得到一个非零值,并返回false(如下所示)。仅此而已。剩下的--程序“崩溃”--直到autodie
,它抛出一个异常。如果没有autodie
(或致命警告)我得到了
Command exit status: 13
,所以close
没有返回true,而$!
为false。如果文件句柄来自一个管道打开,如果其他系统调用失败或其程序以非零状态退出,则
close
返回false。如果唯一的问题是程序以非零状态退出,则$!
将被设置为0
。我们确实得到了一个信号,
13
(对于sigpipe
,请参见man 7 signal
),它终止了程序,因此没有特定的退出代码($? >> 8
确实是零),也没有内核被转储($? & 128
是零)。由于退出状态为非零,
close
返回false,并且autodie
抛出其异常。那么这件事该怎么办?
当然,那个
close
必须留下来检查。即使发送到
zcat
的SIGPIPE
可以被忽略,正如我在一些文档中看到的那样,您也不会希望这样做--它是故意放在那里的,让编写者知道没有读取器,以便它可以停止!最后,是
autodie
终止程序,并且可以按词法禁用它。(这满足了注解中对autodie
的“需要”。)因此,将此管道阅读和early close放在一个块中不要忘记适当地调整此代码中的其他错误处理。
(我有过
no autodie
“泄漏”超出其范围的奇怪经历,请参见here。但那是另一种情况,并由autodie
2.30修复,所以希望不用担心。)另一个选择是将所有这些,或者仅仅是
close()
, Package 在eval
中。这被认为是一个很好的实践,他们在文档中说。然后看看如何处理autodie异常。