在 Linux 中将 STDERR 重定向到来自 Perl 的文件

Redirect STDERR to a file from Perl in Linux

因此,我尝试使用 Perl 从 运行ning 一些基本的 Linux 命令中捕获错误消息。例如,当 运行 执行 ldd 命令时,我尝试捕获 STDERR

# 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 解析字符串中意外的元字符(包括space 个字符)。它只实现了 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 输出足够的量,这可能 运行 进入死锁。我强烈建议使用 Capture::Tiny 代替上面的方法,或者使用 IPC::Run or IPC::Run3 以获得更大的灵活性。