因此,我试图使用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输出足够的数量,则可能会遇到死锁。我强烈建议如上所述使用Capture :: Tiny,或者使用IPC::Run或IPC::Run3以获得更大的灵活性。