在 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 以获得更大的灵活性。
因此,我尝试使用 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 以获得更大的灵活性。