从perl脚本运行Unix命令

Dev*_*per 0 shell perl

我试图在服务器上执行ssh,然后执行grep以获取日志文件中不同错误的计数.一旦我将这些细节记录到CSV文件中.但是当我试图运行grep命令时,我收到错误.

#!/usr/bin/perl
my $addr = "user\@servername";
my $text = qq|Internal Server Error|;
my $remote_path = "/data/logs/error";
my $cmd = `ssh $remote_addr "grep -a $text $remote_path | awk -F " " '{print $4}' | sort -nr | uniq -c | sort -nr 2>/dev/null"`;
print $cmd;
Run Code Online (Sandbox Code Playgroud)

但是当我运行脚本时,我遇到了错误

grep: Internal: No such file or directory
grep: Server: No such file or directory
grep: Error: No such file or directory
Run Code Online (Sandbox Code Playgroud)

有什么建议我们如何在shell脚本中执行此操作.

zdi*_*dim 6

首先,为了避免引用噩梦和shell注入的机会,我建议使用一个模块,比如String :: ShellQuote

然后,我没有看到你需要所有这些外部工具,而这么长的管道是棘手和昂贵的.它调用了许多程序,用于在Perl中完成的工作,并且需要非常精确的语法.

除了ssh-ing 之外,外部工具在这里可能有用的另一件事是提取感兴趣的行grep,如果文件很大(否则你可以将它读成标量).

use warnings;
use strict;
use feature 'say';

use List::Util qw(uniq);  # in List::MoreUtils prior to module v1.45
use String::ShellQuote qw(shell_quote);

my $remote_addr = ...
my $remote_path = ...
my $text = 'Internal Server Error';

my $remote_cmd = shell_quote('grep', '-a', $text, $remote_path);
my $cmd = shell_quote('ssh', $remote_addr, $remote_cmd);

my @lines = qx($cmd);
chomp @lines;

# Process @lines as needed, perhaps
my @result = sort { $b <=> $a } uniq map { (split)[3] } @lines;
say for @result;
Run Code Online (Sandbox Code Playgroud)

一旦运行外部命令,有很多选择.首先考虑使用模块.它们都简化了很多事情,尤其是错误检查,并且通常更可靠,而有些也使更难的工作变得更易于管理.

IPC :: System :: Simple的一个例子

use IPC::System::Simple qw(capturex); 

my @lines = capturex('ssh', $remote_addr, $remote_cmd);
Run Code Online (Sandbox Code Playgroud)

由于ssh在运行时执行命令,因此不需要shell(对于该部分),因此capturex使用.有关更多选项以及如何检查错误,请参阅文档.

其他一些选项,从简单到强大,是Capture :: Tiny,IPC :: Run3,IPC :: Run.

有关所有这些的更多信息,请参阅此帖子中汇编的链接(并搜索更多内容).


我不能看到需要运行该管道,因为它是但是如果有一个(留在远程主机上?)然后形成如上所述的命令,然后组装完整的管道

my $cgrep = shell_quote('grep', '-a', $text, $remote_path);
my $cawk  = shell_quote('awk', '-F', ' ', '{print $4}');
my $csort = shell_quote('sort', '-nr');
my $cuniq = shell_quote('uniq', '-c');

my $remote_cmd = "$cgrep | $cawk | $csort | $cuniq | $csort 2>/dev/null";
Run Code Online (Sandbox Code Playgroud)

请注意,|不应引用所需的shell功能(和重定向).

awk件作品中的空间可能看起来很笨拙但是因为它被躲过了,所以它最终会被卷起来-F.对我来说,这是在shell管道中运行外部程序时出现问题的另一个迹象; 感谢Charles Duffy的评论,我无法弄清楚那个光秃秃的空间.

在这种情况下,管道的部分sortuniq部分可以只键入一个字符串,因为它只是程序名称和选项,但只要进行更改或任何变量进入,就变得棘手.所以我使用shell_quote,为了一致性和作为模板.


据报道模块缺失且难以获得.然后逃避需要转义的东西(直到你弄清楚如何获取模块,即).在这种情况下,几乎没有什么可以解决的,但是这个位可以作为通过复杂管道的常见箍的一个例子.

$text需要达到的字符串grep,一个字符串.因为它通过shell,它会将空间分解为单词,我们需要保护(引用/转义)这些空格.不要忘记,我们还需要通过Perl的解析规则将它首先放到shell中.

单程

my $text_quoted = q(') . quotemeta($text) . q(');
Run Code Online (Sandbox Code Playgroud)

其中,quotemeta也引用了各种其他内容.

我们还应该保护文件名模式,因为它可能依赖shell元字符(如*)

my $remote_path_quoted = quotemeta $remote_path;
Run Code Online (Sandbox Code Playgroud)

但同样,你必须检查这是否适合每种情况.

注意   如果在这些命令中插入任何动态生成的变量(计算的,来自用户......),则需要对其进行验证,并仔细转义和引用.

现在您的管道应该工作(它在我的模拟测试中)

my $cmd = "ssh $remote_host grep -a $text_quoted $remote_path_quoted"
    . q( | awk F ' ' '{print $4}' | sort -nr | uniq | sort -nr 2>/dev/null);
Run Code Online (Sandbox Code Playgroud)

这可以在他们自己的变量等中分解成合理的组件,但我真的不建议这样的手动修补解决方案.

我建议只使用第一部分(ssh + grep),然后在Perl中完成其余部分,如答案的主要部分.然后安装这些模块并切换到它们.

没有(很多)库,没有主要的计算工具可以工作,每个生产安装都包含很多"附加"的东西.随着对更多库的需求的出现,它们被安装.为什么它与Perl不同?是的,你可以只使用内置功能,但这可能会更难.


sort当文件很大时,   一个很好的理由是使用系统,因为它不必一次加载整个文件,也不需要加载速度.但是,在此管道中,它通过管道输入数据并重复调用,因此这些优点不适用.