如何从 Perl 的外部命令中丢弃 STDERR? [英] How to discard STDERR from an external command in Perl?

查看:47
本文介绍了如何从 Perl 的外部命令中丢弃 STDERR?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想捕获外部命令的退出代码,同时用自定义错误消息替换其标准错误输出.

I want to capture exit code of an external command while replacing its standard error output with a custom error message.

my $ret = system("which mysql");
if ($ret != 0) {
    say "Error";
}

如果 mysql 可执行文件不存在,它会显示 which 命令错误消息,这是我不想要的.如何摆脱它?

If mysql executable is not there, it shows which command error message, which I don’t want. How to get rid of it?

推荐答案

参见 http://perldoc.perl.org/perlfaq8.html#How-can-I-capture-STDERR-from-an-external-command%3f

运行外部命令的三种基本方式:

There are three basic ways of running external commands:

system $cmd;        # using system()
$output = `$cmd`;       # using backticks (``)
open (PIPE, "cmd |");   # using open()

使用 system() 时,STDOUT 和 STDERR 将与脚本的 STDOUT 和 STDERR 位于同一位置,除非 system() 命令重定向它们.反引号和 open() 仅读取命令的 STDOUT.您还可以使用 IPC::Open3 中的 open3() 函数.Benjamin Goldberg 提供了一些示例代码:捕获程序的 STDOUT,但丢弃其 STDERR:

With system(), both STDOUT and STDERR will go the same place as the script's STDOUT and STDERR, unless the system() command redirects them. Backticks and open() read only the STDOUT of your command. You can also use the open3() function from IPC::Open3 . Benjamin Goldberg provides some sample code: To capture a program's STDOUT, but discard its 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);

捕获程序的 STDERR,但丢弃其 STDOUT:

To capture a program's STDERR, but discard its 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);

捕获程序的 STDERR,并让其 STDOUT 进入我们自己的 STDERR:

To capture a program's STDERR, and let its STDOUT go to our own STDERR:

use IPC::Open3;
use Symbol qw(gensym);
my $pid = open3(gensym, ">&STDERR", \*PH, "cmd");
while( <PH> ) { }
waitpid($pid, 0);

要分别读取命令的 STDOUT 和 STDERR,您可以将它们重定向到临时文件,让命令运行,然后读取临时文件:

To read both a command's STDOUT and its STDERR separately, you can redirect them to temp files, let the command run, then read the temp files:

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> ) {}

但是没有真正需要两者都是临时文件......以下应该也能正常工作,不会出现死锁:

But there's no real need for both to be tempfiles... the following should work just as well, without deadlocking:

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> ) {}

它也会更快,因为我们可以立即开始处理程序的标准输出,而不是等待程序完成.使用其中任何一个,您都可以在调用之前更改文件描述符:

And it'll be faster, too, since we can begin processing the program's stdout immediately, rather than waiting for the program to finish. With any of these, you can change file descriptors before the call:

open(STDOUT, ">logfile");
system("ls");

或者你可以使用 Bourne shell 文件描述符重定向:

or you can use Bourne shell file-descriptor redirection:

$output = `$cmd 2>some_file`;
open (PIPE, "cmd 2>some_file |");

您还可以使用文件描述符重定向使 STDERR 成为 STDOUT 的副本:

You can also use file-descriptor redirection to make STDERR a duplicate of STDOUT:

$output = `$cmd 2>&1`;
open (PIPE, "cmd 2>&1 |");

请注意,您不能简单地在 Perl 程序中打开 STDERR 作为 STDOUT 的副本,并避免调用 shell 进行重定向.这不起作用:

Note that you cannot simply open STDERR to be a dup of STDOUT in your Perl program and avoid calling the shell to do the redirection. This doesn't work:

open(STDERR, ">&STDOUT");
$alloutput = `cmd args`;  # stderr still escapes

这会失败,因为 open() 使 STDERR 转到 open() 时 STDOUT 所在的位置.然后反引号使 STDOUT 转到字符串,但不要更改 STDERR(它仍然转到旧的 STDOUT).请注意,您必须在反引号中使用 Bourne shell (sh(1) ) 重定向语法,而不是 csh(1) !关于为什么 Perl 的 system() 和反引号和管道打开都使用 Bourne shell 的详细信息在Far More Than You Ever Wanted To Know"中的 vs/csh.whynot 文章中.http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz.一起捕获命令的 STDERR 和 STDOUT:

This fails because the open() makes STDERR go to where STDOUT was going at the time of the open(). The backticks then make STDOUT go to a string, but don't change STDERR (which still goes to the old STDOUT). Note that you must use Bourne shell (sh(1) ) redirection syntax in backticks, not csh(1) ! Details on why Perl's system() and backtick and pipe opens all use the Bourne shell are in the versus/csh.whynot article in the "Far More Than You Ever Wanted To Know" collection in http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz . To capture a command's STDERR and STDOUT together:

$output = `cmd 2>&1`;                       # either with backticks
$pid = open(PH, "cmd 2>&1 |");              # or with an open pipe
while (<PH>) { }                            #    plus a read

捕获命令的 STDOUT 但丢弃其 STDERR:

To capture a command's STDOUT but discard its 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

捕获命令的 STDERR 但丢弃其 STDOUT:

To capture a command's STDERR but discard its 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:

To exchange a command's STDOUT and STDERR in order to capture the STDERR but leave its STDOUT to come out our old 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,最简单的方法是将它们分别重定向到文件,然后在程序完成后从这些文件中读取:

To read both a command's STDOUT and its STDERR separately, it's easiest to redirect them separately to files, and then read from those files when the program is done:

system("program args 1>program.stdout 2>program.stderr");

排序在所有这些示例中都很重要.这是因为 shell 严格按照从左到右的顺序处理文件描述符重定向.

Ordering is important in all these examples. That's because the shell processes file descriptor redirections in strictly left to right order.

system("prog args 1>tmpfile 2>&1");
system("prog args 2>&1 1>tmpfile");

第一个命令将标准输出和标准错误发送到临时文件.第二个命令只将旧标准输出发送到那里,旧标准错误显示在旧标准输出上.

The first command sends both standard out and standard error to the temporary file. The second command sends only the old standard output there, and the old standard error shows up on the old standard out.

这篇关于如何从 Perl 的外部命令中丢弃 STDERR?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆