`最后读取外部命令崩溃的文件时perl

发布于 2025-02-13 09:51:35 字数 1008 浏览 0 评论 0原文

我有以下代码:

#!/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

,但是,如果我评论上次代码运行没有问题,则该文件为 gumet ,这使得运行时间有显着差异。

如果我删除关闭$ view,则代码也有效,但是CLOSE是正确的练习。

如何使用last关闭$ view运行代码?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦里兽 2025-02-20 09:51:35

当您最后读取该过程(zcat在此处)和关闭管道,在过程完成之前,该过程获取sigpipe

在另一端写入该过程之前关闭管道的读取末端,在写作中,在收到sigpipe的作者中

因此close> close> close然后等待 s上它获得非零,并返回false(如下所示)。就这样。其余的 - 程序“崩溃” - 最多可autodie,它引发了异常。没有autodie(或致命警告)

use warnings;
use strict;
use feature 'say';

use Scalar::Util qw(openhandle);

my $file = shift // die "Usage: $0 file\n";

open my $view, "zcat $file |" or die "Can't pipe-open zcat: $!";

while (<$view>) {
    next unless /^#CHROM\t/;  # (added to my test file)
    say "Matched --> $_";
    last;
}

say "Filehandle good? --> ", openhandle($view) // 'undef';  # GLOB(...)
    
close $view or warn $!  
    ? "Error closing pipe: $!" 
    : "Command exit status: $?";                            # 13

say "Program received a signal ", $? & 127  if $? & 127;    # 13

我获得命令退出状态:13,关闭 $!$! /代码>是错误的。这表明唯一的问题是非零状态

如果FileHandle来自管道打开,则关闭如果涉及的其他SYSCALL中的一个失败或其程序以非零状态退出,则返回false。如果唯一的问题是该程序已退出非零,则$!将设置为0

我们确实收到了一个信号,13(对于sigpipe,请参见Man 7 Signal),哪些终止程序,因此没有特定的退出代码($?&gt;&gt; 8的确是零),并且没有抛弃核心($?&amp; 128是零)。请参阅 $?在perlvar

因为退出状态为非零关闭返回false和autodie引发其异常。


那该怎么办?

当然,关闭必须保留并检查。

即使sigpipe发送到zcat的也可以忽略作者知道没有读者可以停止!

最后,杀死程序的autodie可以通过词法禁用。 (这满足了评论中指定的autodie的“需求”。)因此,将此管道读数放在块中,

READ_FROM_PIPE: { 
    no autodie qw(close);
    # do the pipe-open, read from the pipe, close() it...
};
# rest of code 

请不要忘记在此代码中适当调整您的其他错误处理。

(我在no autodie“泄漏”范围中有奇怪的经历,请参见在这里

CLOSS(),在eval中而是。他们在文档中说,这被认为是好的做法。然后查看如何使用 autodie Exceptions

When you last out of reading that process (zcat here) and close the pipe, before the process is done writing, the process gets a SIGPIPE

Closing the read end of a pipe before the process writing to it at the other end is done writing results in the writer receiving a SIGPIPE

So as close then waits on it it gets a non-zero, and returns false (as seen below). That's all. The rest -- the program "crashing" -- is up to autodie, which throws an exception. Without autodie (or fatal warnings)

use warnings;
use strict;
use feature 'say';

use Scalar::Util qw(openhandle);

my $file = shift // die "Usage: $0 file\n";

open my $view, "zcat $file |" or die "Can't pipe-open zcat: $!";

while (<$view>) {
    next unless /^#CHROM\t/;  # (added to my test file)
    say "Matched --> $_";
    last;
}

say "Filehandle good? --> ", openhandle($view) // 'undef';  # GLOB(...)
    
close $view or warn $!  
    ? "Error closing pipe: $!" 
    : "Command exit status: $?";                            # 13

say "Program received a signal ", $? & 127  if $? & 127;    # 13

I get Command exit status: 13, so close didn't return true while $! is false. This indicates that the only issue was the non-zero status

If the filehandle came from a piped open, close returns false if one of the other syscalls involved fails or if its program exits with non-zero status. If the only problem was that the program exited non-zero, $! will be set to 0.

We did get a signal, 13 (for sigpipe, see man 7 signal), what terminated the program and so there was no particular exit code ($? >> 8 is indeed zero), and no core was dumped ($? & 128 is zero). See $? in perlvar

Since the exit status is non-zero close returns false and autodie throws its exception.


So what to do with this?

That close must stay and be checked, of course.

Even if the SIGPIPE sent to zcat could 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 autodie that kills the program, and it can be disabled lexically. (This satisfies the "need" for autodie stated in a comment.) So put this pipe reading and early close in a block

READ_FROM_PIPE: { 
    no autodie qw(close);
    # do the pipe-open, read from the pipe, close() it...
};
# rest of code 

Don'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 by autodie 2.30 so hopefully not of concern.)

Another option is to wrap all this, or just the close(), in eval instead. This is considered good practice, they say in docs. Then see how to work with autodie exceptions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文