Perl:`die` 在使用 gzip 打开不存在的 gz 文件时不起作用
Perl: `die` did not work upon opening a nonexistent gz file using gzip
以下脚本创建一个名为 "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
语句?
它被埋在 perlipc 中,但这似乎是相关的(强调已添加):
Be careful to check the return values from both open() and close(). If you're writing to a pipe, you should also trap SIGPIPE. Otherwise, think of what happens when you start up a pipe to a command that doesn't exist: the open() will in all likelihood succeed (it only reflects the fork()'s success), but then your output will fail--spectacularly. Perl can't know whether the command worked, because your command is actually running in a separate process whose exec() might have failed. Therefore, while readers of bogus commands return just a quick EOF, writers to bogus commands will get hit with a signal, which they'd best be prepared to handle.
改为使用 IO::Uncompress::Gunzip
读取 gzip 文件。
open 文档明确说明了 open
-ing 一个过程,因为这确实不同
If you open a pipe on the command -
(that is, specify either |-
or -|
with the one- or two-argument forms of open
), an implicit fork
is done, so open
returns twice: in the parent process it returns the pid
of the child process, and in the child process it returns (a defined) 0
. Use defined($pid)
or //
to determine whether the open
was successful.
For example, use either
my $child_pid = open(my $from_kid, "-|") // die "Can't fork: $!";
or
my $child_pid = open(my $to_kid, "|-") // die "Can't fork: $!";
(后面的代码显示了它的一种用法,您不需要)主要是检查 defined
——根据设计,如果 [=13,我们会得到 undef
=] 因为进程失败,而不仅仅是任何 "false."
虽然这应该更正,但请记住 open
调用失败 如果 fork
本身失败 ,这是罕见的;在大多数情况下,当 "command fails" 时 fork
成功,但后来却没有。因此,在这种情况下,我们无法获得 // die
消息,但最终会看到来自 shell 或命令或 OS 的消息,希望如此。
这没关系,如果信息性消息确实由进程的某些部分发出。将整个内容包装在 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: $!";
注意其他几点
命令的"list form"绕过了shell
词法文件句柄 (my $fh
) 比 typeglobs (IN
)
好得多
打印die
语句中的实际错误,在$!
variable
中
检查 close 以最终检查一切进展情况
以下脚本创建一个名为 "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
语句?
它被埋在 perlipc 中,但这似乎是相关的(强调已添加):
Be careful to check the return values from both open() and close(). If you're writing to a pipe, you should also trap SIGPIPE. Otherwise, think of what happens when you start up a pipe to a command that doesn't exist: the open() will in all likelihood succeed (it only reflects the fork()'s success), but then your output will fail--spectacularly. Perl can't know whether the command worked, because your command is actually running in a separate process whose exec() might have failed. Therefore, while readers of bogus commands return just a quick EOF, writers to bogus commands will get hit with a signal, which they'd best be prepared to handle.
改为使用 IO::Uncompress::Gunzip
读取 gzip 文件。
open 文档明确说明了 open
-ing 一个过程,因为这确实不同
If you open a pipe on the command
-
(that is, specify either|-
or-|
with the one- or two-argument forms ofopen
), an implicitfork
is done, soopen
returns twice: in the parent process it returns thepid
of the child process, and in the child process it returns (a defined)0
. Usedefined($pid)
or//
to determine whether theopen
was successful.For example, use either
my $child_pid = open(my $from_kid, "-|") // die "Can't fork: $!";
or
my $child_pid = open(my $to_kid, "|-") // die "Can't fork: $!";
(后面的代码显示了它的一种用法,您不需要)主要是检查 defined
——根据设计,如果 [=13,我们会得到 undef
=] 因为进程失败,而不仅仅是任何 "false."
虽然这应该更正,但请记住 open
调用失败 如果 fork
本身失败 ,这是罕见的;在大多数情况下,当 "command fails" 时 fork
成功,但后来却没有。因此,在这种情况下,我们无法获得 // die
消息,但最终会看到来自 shell 或命令或 OS 的消息,希望如此。
这没关系,如果信息性消息确实由进程的某些部分发出。将整个内容包装在 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: $!";
注意其他几点
命令的"list form"绕过了shell
词法文件句柄 (
my $fh
) 比 typeglobs (IN
) 好得多
打印
die
语句中的实际错误,在$!
variable 中
检查 close 以最终检查一切进展情况