考虑:
#!/usr/bin/perl
use strict;
use warnings;
my $command = "nosuchcommand"
my $rc = open( my $fh, "-|", "$command" );
if ( ! defined( $rc ) )
{
print "My own error message\n";
}
else
{
system( "ps -p $rc" );
}
预期输出:
My own error message
观察到的输出:
Can't exec "nosuchcommand": No such file or directory at ./mcre line 8.
My own error message
我如何捕捉/捕获/抑制Perl在失败的open-pipe上生成的错误消息(有利于我自己的错误处理)?
进一步的观察:
- 如果我
open( my $fh, "-|", "$command 2>&1" );
,我得到预期的输出。但是如果$command
成功,我会将$command
的STDERR
混合到STDOUT
中,我不希望这样。 - 如果我
open( my $fh, "-|", "$command 2>/dev/null" );
,我得到这个:
PID TTY TIME CMD
15143 pts/0 00:00:00 sh <defunct>
所以2>/dev/null
的 specific redirection(而不是2>&1
)改变了open
的返回值。这有点令人惊讶,但不是我问题的核心。
IPC::运行
到目前为止,已经有两个答案提出了这一点。我知道在一般情况下这可能是“正确”的答案,但我真的不想这样做(因为IPC::Run
在一个非常重要的目标平台上不是一个选项,这个平台没有它,也没有提供简单的安装方法)。
3条答案
按热度按时间1zmg4dgp1#
该消息不是来自Perl,而是来自您启动以执行shell命令的shell。[1]这是因为
相当于
正如您所建议的,您可以使用
2>/dev/null
重定向STDERR,但这也会使程序中的错误消失(当使用有效程序时)。你可以这样做:
如果你想要更多的控制权,你需要自己实现。IPC::Open3的
open3
源代码将是一个很好的开始。1.除了Perl优化了shell的使用,使其不再是简单的shell命令,所以消息实际上确实来自Perl。但这在这里不是一个有用的区分。
tjrkku2a2#
至于shell中的重定向,我们可以将
STDERR
单独重定向到文件(as以及任何文件描述符) 这会使
STDOUT
退出,如您所希望的那样在管道中打开,或者重定向它(cmd > stdout.out 2> stderr.out
)。该机制可以在Perl代码中使用,因为命令中的shell元字符(如重定向符号)使用shell。† Perl使用
sh
,但它通常链接到另一个shell,因此请检查您的发行版中使用的是哪个shell而不是sh
。(或者通过bash
显式运行。)举个例子
所以这确实捕获了
STDERR
流,而且是单独捕获的,但是还有一个文件要处理。‡open
的返回值是子进程的pid(这里是shell,不管命令是否可以由它启动),除非fork
本身失败。§检查close可以了解事情的进展情况,并提示可能出现的问题--命令无法启动,它报告非零状态等如果文件句柄来自一个管道打开,如果涉及的其他系统调用之一失败,或者如果它的程序以非零状态退出,
close
将返回false。...在这里,命令(
ls
)成功启动,并打印*txt
文件(如果有的话)和退出代码(在我的系统上是2
)。在STDERR
流中出现的关于不存在的nofile
的消息将进入err.out
文件。如果命令本身无法启动,则只打印退出代码(
127
),而STDERR
消息再次出现在文件中。我们不能直接判断重定向到文件的消息是关于程序(不存在或无法启动)还是来自程序本身,因为它打印到
STDERR
。但是我们确实将该错误保存在一个文件中,以便可以更仔细地调查它,而不像将其发送到/dev/null
。最后,我也会推荐IPC::Run。基本的使用是微不足道的,而如果您需要的话,它几乎是一个迷你shell。
†外部命令可以通过启动shell并向其传递带有命令和参数的字符串来启动,或者通过使用
execvp
函数之一直接启动命令。如果命令没有shell“元字符”(重定向、globbing、引号等),则通常通过避免shell来优化该过程。这可以通过语法确保,参见system和exec。否则,启动shell,以便能够执行指定的操作。这显然是有区别的:在shell中,命令也经历了它的语法规则,我们可以得到shell的消息,在pipe-open中,一个进程 is forked用于shell(
fork
通常不会失败),然后另一个用于命令,等等。没有shell,我们可以得到perl关于(失败的)命令的消息。在任何一种情况下,我们也可以从程序本身获得消息。如果不进入细节或使用库(并进入细节),发生的事情通常不容易解开。‡我用内置工具在一个语句中读取文件(并将其保存到变量中),因为提到了非核心模块的问题。如果安装软件实际上不是一个问题,那么有很好的库可以解决这个问题。比如说
§ open docs中的一个例子暗示了一个无法启动的命令会导致
open
返回false
。这只适用于那些不涉及shell元字符的命令,因此直接启动的命令--因此,如果它们启动失败,则确实没有进程。另请参阅perlipc中的此主题,示例中有更准确的错误消息。
jgovgodb3#
这一行实际上是一个“警告”,由
use warnings;
启用。perldiag:无法执行“%s”:%s
(W exec)system()、exec()或管道打开调用无法执行指定的程序,原因如下。典型原因包括:文件的权限错误,在$ENV{PATH}中找不到文件,有问题的可执行文件是为另一个体系结构编译的,或者#!脚本中的一行指向由于类似原因而无法运行的解释器。(或者您的系统不支持#!at all.)
现在,
use warnings;
是 * 强烈 * 推荐的,所以完全删除它并不是一个“解决方案”。但是可以通过no warnings <category>
在给定范围内禁用警告。因此,为了抑制该特定消息:输出:
底层shell生成的错误消息(“No such file or directory”)部分位于
$!
特殊变量中。请注意,您应该使用open
的四参数版本(如果需要,可以使用空LIST,如上图所示)。Perl有不同的执行外部命令的方法,一种是通过/bin/sh -c
(并且不受exec
警告的控制),另一种是通过execvp
(并且受警告的控制)。open
的四参数版本确保Perl将使用后者。