I have the following code:
#!/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;
the above code crashes with the error
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
However, if I comment out last the code runs without a problem, however, the file is huge and this makes a significant difference in running time.
The code also works if I remove close $view but close is proper practice.
How can I run the code with both last and close $view?
When you
lastout of reading that process (zcathere) and close the pipe, before the process is done writing, the process gets aSIGPIPESo as
closethenwaits on it it gets a non-zero, and returns false (as seen below). That's all. The rest -- the program "crashing" -- is up toautodie, which throws an exception. Withoutautodie(or fatal warnings)I get
Command exit status: 13, soclosedidn't return true while$!is false. This indicates that the only issue was the non-zero statusWe did get a signal,
13(forsigpipe, seeman 7 signal), what terminated the program and so there was no particular exit code ($? >> 8is indeed zero), and no core was dumped ($? & 128is zero). See $? in perlvarSince the exit status is non-zero
closereturns false andautodiethrows its exception.So what to do with this?
That
closemust stay and be checked, of course.Even if the
SIGPIPEsent tozcatcould be ignored, as I've seen claimed in some docs, you wouldn't want that -- it's there on purpose, to let the writer know that there are no readers so that it can stop!Finally, it is
autodiethat kills the program, and it can be disabled lexically. (This satisfies the "need" forautodiestated in a comment.) So put this pipe reading and early close in a blockDon't forget to suitably adjust your other error handling in this code.
(I've had weird experience with
no autodie"leakage" out of its scope, see here. But that was a different case, and fixed byautodie2.30 so hopefully not of concern.)Another option is to wrap all this, or just the
close(), inevalinstead. This is considered good practice, they say in docs. Then see how to work with autodie exceptions.