在Linux中将STDERR从Perl重定向到文件
收藏

所以,我试图通过使用Perl运行一些基本的Linux命令来捕捉错误消息。例如,我在运行STDERR命令时尝试捕捉ldd

# The stderr_file already exists
my $cmd = "ldd $file 2>$stderr_file";
my $output = `$cmd`;

但是,即使ldd命令的输出确实包含ldd: warning: you do not have execution permission for之类的错误消息,它也不会将它们打印到$stderr_file中,我想知道为什么。
然后我试图自己运行命令:ldd /some/path/to/file 2>./error.log,但失败了:ldd: ./2: No such file or directory
我怀疑原因是因为我的Linux使用了Tcsh,因为如果我切换到Bash,命令就可以工作。
我应该如何处理和解决这个问题?
另外,我读了一些以前的线程,但没有找到任何相关的线程或方法来解决它。


最佳答案:

在将字符串插入要作为单个参数的shell命令时,应始终使用String::ShellQuote以避免shell解析字符串中的意外元字符(包括空格字符)时出现错误。不过,它只实现了bourne shell引用,因此它也可能与tcsh不兼容,但Perl通常配置为使用/bin/sh,这应该与bourne shell兼容。

use strict;
use warnings;
use String::ShellQuote;
my $cmd = 'ldd ' . shell_quote($file) . ' 2>' . shell_quote($stderr_file);

作为替代方案,您可以使用system()的列表形式并在Perl中重定向STDERR,从而完全避免使用shell。Capture::Tiny这很简单。
use strict;
use warnings;
use Capture::Tiny 'capture';
use Path::Tiny;
my ($out, $err, $exit_code) = capture { system 'ldd', $file };
# error checking for system() call here
path($stderr_file)->spew_raw($err);

(Path::Tiny只是一个例子,您也可以使用File::Slurper或打开文件并通过适当的错误检查自行写入。)
核心模块IPC::Open3也可以用于单独捕获STDERR并避免使用shell,更手动一些。
use strict;
use warnings;
use IPC::Open3;
use Symbol 'gensym';
my $pid = open3 undef, my $stdout, my $stderr = gensym, 'ldd', $file;
my ($out, $err);
{
  local $/;
  $out = readline $stdout;
  $err = readline $stderr;
}
waitpid $pid, 0;
my $exit_code = $? >> 8;

如果进程向STDERR输出足够的量,这可能会导致死锁。我强烈建议使用捕获::微代替以上,或是< > >或> > >以获得更大的灵活性。

    公众号
    关注公众号订阅更多技术干货!