因此,我试图使用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
中,我不知道为什么。 / p>
然后,我尝试自己运行该命令:ldd /some/path/to/file 2>./error.log
,但失败并显示:ldd: ./2: No such file or directory
。
我怀疑原因是由于我的Linux使用Tcsh
,因为如果我切换到Bash
,则该命令有效。
我应该如何解决这个问题?
此外,我阅读了以前的一些主题,但是没有找到任何相关的主题或方法来解决它。
答案 0 :(得分:2)
在将字符串插入要用作单个参数的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并避开外壳,这要多手动一些。
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以获得更大的灵活性。