我想捕获外部命令的退出代码,同时用自定义错误消息替换其标准错误输出。如何从Perl的外部命令中丢弃STDERR?
my $ret = system("which mysql");
if ($ret != 0) {
say "Error";
}
如果mysql
可执行文件是不存在的,它显示which
命令错误信息,我不想要。如何摆脱它?
我想捕获外部命令的退出代码,同时用自定义错误消息替换其标准错误输出。如何从Perl的外部命令中丢弃STDERR?
my $ret = system("which mysql");
if ($ret != 0) {
say "Error";
}
如果mysql
可执行文件是不存在的,它显示which
命令错误信息,我不想要。如何摆脱它?
见http://perldoc.perl.org/perlfaq8.html#How-can-I-capture-STDERR-from-an-external-command%3f
存在正在运行的外部命令的三种基本方法:
system $cmd; # using system()
$output = `$cmd`; # using backticks (``)
open (PIPE, "cmd |"); # using open()
随着系统(),标准输出和STDERR会去同一个地方为脚本的STDOUT和STDERR,除非系统()命令重定向他们。反引号和open()只读取命令的标准输出。 您也可以使用IPC :: Open3中的open3()函数。本杰明·戈德堡提供了一些示例代码: 为了捕捉一个程序的标准输出,而放弃其STDERR:
use IPC::Open3;
use File::Spec;
use Symbol qw(gensym);
open(NULL, ">", File::Spec->devnull);
my $pid = open3(gensym, \*PH, ">&NULL", "cmd");
while(<PH>) { }
waitpid($pid, 0);
为了捕捉一个程序的标准错误,但放弃其STDOUT:
use IPC::Open3;
use File::Spec;
use Symbol qw(gensym);
open(NULL, ">", File::Spec->devnull);
my $pid = open3(gensym, ">&NULL", \*PH, "cmd");
while(<PH>) { }
waitpid($pid, 0);
为了捕捉一个程序的标准错误,并让其STDOUT去我们自己的STDERR:
use IPC::Open3;
use Symbol qw(gensym);
my $pid = open3(gensym, ">&STDERR", \*PH, "cmd");
while(<PH>) { }
waitpid($pid, 0);
要单独阅读这两个命令的STDOUT和STDERR,您可以将其重定向到临时文件,L等运行的命令,然后读取临时文件:
use IPC::Open3;
use Symbol qw(gensym);
use IO::File;
local *CATCHOUT = IO::File->new_tmpfile;
local *CATCHERR = IO::File->new_tmpfile;
my $pid = open3(gensym, ">&CATCHOUT", ">&CATCHERR", "cmd");
waitpid($pid, 0);
seek $_, 0, 0 for \*CATCHOUT, \*CATCHERR;
while(<CATCHOUT>) {}
while(<CATCHERR>) {}
但是有两个是临时文件没有真正的需要......下面应该工作一样,没有死锁:
use IPC::Open3;
use Symbol qw(gensym);
use IO::File;
local *CATCHERR = IO::File->new_tmpfile;
my $pid = open3(gensym, \*CATCHOUT, ">&CATCHERR", "cmd");
while(<CATCHOUT>) {}
waitpid($pid, 0);
seek CATCHERR, 0, 0;
while(<CATCHERR>) {}
而且它也会更快,因为我们可以立即开始处理程序的stdout,而不是等待程序完成。 与任何这些,你可以在呼叫前更改文件描述符:
open(STDOUT, ">logfile");
system("ls");
,或者您可以使用Bourne shell的文件描述符重定向:
$output = `$cmd 2>some_file`;
open (PIPE, "cmd 2>some_file |");
你也可以用档案描述重定向使STDERR一个STDOUT的重复:
$output = `$cmd 2>&1`;
open (PIPE, "cmd 2>&1 |");
请注意,你不能简单地将STDERR开成STDOUT的复制在你的Perl程序,避免调用壳里做的REDI rection。这不起作用:
open(STDERR, ">&STDOUT");
$alloutput = `cmd args`; # stderr still escapes
失败的原因是开放的()让STDERR转至STDOUT打算在开放时间()。反引号会使STDOUT转到一个字符串,但不要更改STDERR(它仍然会转到旧的STDOUT)。 请注意,您必须在反引号中使用Bourne shell(sh(1))重定向语法,而不是csh(1)!关于为什么Perl的system()以及反向和管道打开都使用Bourne shell的详细信息,请参见http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz中“远远超过你想知道的”集合中的vs/csh.whynot文章。要捕获一个命令的STDERR和STDOUT在一起:
$output = `cmd 2>&1`; # either with backticks
$pid = open(PH, "cmd 2>&1 |"); # or with an open pipe
while (<PH>) { } # plus a read
要捕获一个命令的标准输出,但是却丢弃其STDERR:
$output = `cmd 2>/dev/null`; # either with backticks
$pid = open(PH, "cmd 2>/dev/null |"); # or with an open pipe
while (<PH>) { } # plus a read
要捕获一个命令的标准错误,但是却丢弃其STDOUT:
$output = `cmd 2>&1 1>/dev/null`; # either with backticks
$pid = open(PH, "cmd 2>&1 1>/dev/null |"); # or with an open pipe
while (<PH>) { } # plus a read
要交换命令的STDOUT和STDERR以捕获STDERR,但保留其STDOUT出来我们的旧STDERR:
$output = `cmd 3>&1 1>&2 2>&3 3>&-`; # either with backticks
$pid = open(PH, "cmd 3>&1 1>&2 2>&3 3>&-|");# or with an open pipe
while (<PH>) { } # plus a read
阅读这两个命令的STDOUT和STDERR分别,最简单的方法将它们分别重定向到文件,然后从这些文件中读取当程序进行:
system("program args 1>program.stdout 2>program.stderr");
排序在所有这些重要例子。这是因为shell以严格从左至右的顺序处理文件描述符重定向。
system("prog args 1>tmpfile 2>&1");
system("prog args 2>&1 1>tmpfile");
第一个命令将标准输出和标准错误都发送到临时文件。第二个命令只在那里发送旧的标准输出,旧的标准错误在旧的标准输出中显示。
一个更现代的Perl方法可能是使用一个模块,如Capture::Tiny
。例如:
#!/usr/bin/perl
use strict;
use warnings;
use Capture::Tiny qw/capture/;
my $ret;
# you may ignore stdout/stderr, if you wish, by commenting out the next line
my ($stdout, $stderr) =
capture {
$ret = system("which mysql");
};
if ($ret!=0) {
print " error " ;
# perhaps even something cool like
# print $stderr if $verbose # where $verbose is set by a command-line flag.
}
相关:http://stackoverflow.com/q/8733131/2157640 – Palec 2014-09-12 15:23:30