我试图做一个服务器上的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;
但我得到以下错误,当我运行该脚本
grep: Internal: No such file or directory
grep: Server: No such file or directory
grep: Error: No such file or directory
是否有任何建议,我们如何在shell脚本做到这一点。
首先,为了避免引用噩梦和很多机会壳注入我建议使用一个模块,如字符串:: 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;
一旦涉及到运行的外部命令,也有很多选择。 首先考虑使用一个模块。 他们都简化了很多东西,特别是与错误检查,而且一般比较可靠,有些还让就业困难远远更易于管理。
用一个例子IPC ::系统::简单
use IPC::System::Simple qw(capturex);
my @lines = capturex('ssh', $remote_addr, $remote_cmd);
由于ssh
当与一个不需要一个外壳(该部分),以便运行执行该命令capturex
被使用。 有关详情,请选项和如何检查错误文件。
一些其他的选择,从简单到更强大,是捕捉::微小 , IPC :: RUN3 , IPC ::运行 。
欲了解更多有关这一切看到组装环节这篇文章 (和寻找更多)。
我看不到一个需要运行的管道,因为它代表†但如果有一个(留在远程主机上的?)然后形成如上命令,然后组装完整的管道
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";
需要注意的是所需要的外壳功能( |
和重定向)不应该被引用了。
在仅仅空间awk
一块可能是尴尬的前瞻性,但因为它被逃脱它卷起适合-F
。 对于我来说,与运行在外壳管道外部程序麻烦的另一个迹象; 我无法弄清楚裸空间,感谢查尔斯·达菲的评论。
在这种情况下, sort
和uniq
管道部分可以刚才输入作为一个字符串,因为它是唯一的程序名称和选项,但只要更改时或任何变量,使他们以这种方式变得非常棘手。 所以我用shell_quote
,一致性和作为模板。
模块失踪,很难获得。 然后逃逸有什么需要进行转义(直到你找出如何让这些模块,这是)。 在这种情况下,正好是小解决,但该位可作为普通篮球的例子复杂的管道要经过。
以字符串$text
需要达到grep
正因为如此,一个字符串。 由于它穿过外壳,这将打破它通过空间分成的话,我们需要保护(报价/退出)的处所。 更不要忘了,我们还需要得到它摆在首位的外壳,通过Perl的解析规则。
单程
my $text_quoted = q(') . quotemeta($text) . q(');
其中quotemeta引用各种其他事情也是如此。
我们也应该保护的文件名模式,因为它可能会依赖于shell元字符(如*
)
my $remote_path_quoted = quotemeta $remote_path;
但同样,你要检查一下是否适合在各种情况下。
注意如果任何动态生成的变量(计算,来自用户的...)都插在这些命令,他们需要被验证,事情仔细逃脱并引述 。
现在你应该管的工作(它在我的模拟试题)
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);
这可以被分解成自己的变量等敏感部件,但我真的不建议这样的手工打补丁的解决方案。
我建议只使用第一部分(SSH + grep的),并完成其余在Perl不过,作为答案的主要部分。 然后安装这些模块,然后切换到它们。
没有大的计算工具,工作没有(许多)库,和每一个生产安装含有大量的“额外”的东西。 由于需要更多的图书馆出现它们的安装。 为什么必须是用Perl不同? 是的,你可以只用内建做,但可能更难。
†一个很好的理由会如此使用系统sort
时,文件是巨大的,因为它没有加载整个文件一次,并且它的速度。 然而,这条管线是在管道提供的数据和重复调用等等这些优势并不适用。