Perl:使用 gzip 打开不存在的 gz 文件时,`die` 不起作用

Posted

技术标签:

【中文标题】Perl:使用 gzip 打开不存在的 gz 文件时,`die` 不起作用【英文标题】:Perl: `die` did not work upon opening a nonexistent gz file using gzip 【发布时间】:2020-03-24 16:06:28 【问题描述】:

以下脚本会创建一个名为“input.gz”的 gzip 文件。然后脚本尝试使用gzip -dc 打开“input.gz”。直观地说,如果提供了错误的输入文件名,则应该触发die。但是,如以下脚本所示,即使提供了错误的输入文件名(“inputx.gz”),程序也不会die

use warnings;
use strict;

system("echo PASS | gzip -c > input.gz");

open(IN,"-|","gzip -dc inputx.gz") || die "can't open input.gz!";

print STDOUT "die statment was not triggered!\n";

close IN;

上面脚本的输出是

die statment was not triggered!
gzip: inputx.gz: No such file or directory

我的问题是:为什么die 语句没有被触发,即使gzip 因错误退出?以及如何在给出错误文件名时触发die 语句?

【问题讨论】:

gzip 退出并出现错误,但 open 没有失败。 【参考方案1】:

它隐藏在perlipc 中,但这似乎是相关的(强调添加):

小心检查 open() 和 close() 的返回值。如果您正在写入管道,您还应该捕获 SIGPIPE。否则,想想当你启动一个不存在的命令的管道时会发生什么:open() 很可能会成功(它只反映了 fork() 的成功),但随后您的输出将失败 - 非常壮观。 Perl 无法知道该命令是否有效,因为您的命令实际上是在一个单独的进程中运行,该进程的 exec() 可能已经失败。因此,虽然虚假命令的读取者只返回一个快速的 EOF,但虚假命令的编写者会受到信号的影响,他们最好做好处理的准备。

改用IO::Uncompress::Gunzip 来读取压缩文件。

【讨论】:

【参考方案2】:

open 文档明确说明了 open-ing 进程,因为这确实不同

如果您在命令- 上打开管道(即,使用open 的一个或两个参数形式指定|--|),则完成一个隐式fork,所以open返回两次:在父进程中返回子进程的pid,在子进程中返回(一个已定义的)0。使用defined($pid)//判断open是否成功。

例如,使用任一

my $child_pid = open(my $from_kid, "-|") // die "Can't fork: $!";

my $child_pid = open(my $to_kid,   "|-") // die "Can't fork: $!";

(后面的代码显示了它的一种用途,你不需要)主要是检查defined——根据设计,如果open进程失败,我们会得到undef,而不是只是任何“假”。

虽然这应该更正,但请记住 open 调用失败如果 fork 本身失败,这是罕见的;在大多数情况下,当“命令失败”时,fork 成功,但后来却没有。因此,在这种情况下,我们无法获得// die 消息,但希望最终能看到来自 shell、命令或操作系统的消息。

不过,如果过程的某些部分确实发出了信息性消息,这没关系。将整个内容包裹在 eval 中,您将获得可管理的错误报告。

但通常很难确保获得所有正确的消息,而且在某些情况下是不可能的。一种好的方法是使用模块来运行和管理外部命令。在许多其他优点中,它们通常还可以更好地处理错误。如果您需要在发出进程的输出时正确处理它,我推荐IPC::Run(否则我也建议这样做)。

阅读链接文档的内容,了解有关您需要什么的具体示例以及非常有用的见解。


你的情况

# Check input, depending on how it is given,
# consider String::ShellQuote if needed
my $file = ...;

my @cmd = ('gzip', '-dc', $file);

my $child_pid = open my $in, '-|', @cmd  
    // die "Can't fork for '@cmd': $!";

while (<$in>)  
    ...

close $in or die "Error closing pipe: $!";

注意其他几点

命令的“列表形式”绕过shell

词法文件句柄 (my $fh) 比 typeglobs (IN) 好得多

die 语句中打印实际错误,在$! variable

检查 close 以了解一切进展情况

【讨论】:

以上是关于Perl:使用 gzip 打开不存在的 gz 文件时,`die` 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

Perl | Perl读取gzip压缩文件

使用管道在 Perl 中将管道文件输出到 gzip 的 Python 等效项

Perl 在打开 gzip 文件时给出“gzip:stdout:Broken pipe”错误,但前提是连接到数据库

前端页面加载速度优化---Ngnix之GZIP压缩

压缩工具gzip,bzip2,xz

如何在Linux解压缩(打开)Gz 文件?