简体   繁体   English

从Perl脚本运行Unix命令

[英]Run Unix command from a perl Script

I am trying to do ssh on a server and then do a grep to get the count of different errors in the log file. 我正在尝试在服务器上执行ssh,然后执行grep以获取日志文件中不同错误的计数。 Once i have those details logs those into a CSV file. 一旦有了这些详细信息,就将它们记录到CSV文件中。 But when i am trying to run the grep command i am getting error. 但是,当我尝试运行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;

But i am getting below error when i am running the script 但是我在运行脚本时遇到错误

grep: Internal: No such file or directory
grep: Server: No such file or directory
grep: Error: No such file or directory

Is there any suggestion how we can do this in shell script. 有没有建议我们如何在shell脚本中做到这一点。

First, in order to avoid a quoting nightmare and the many chances for shell injection I'd suggest to use a module, like String::ShellQuote 首先,为了避免引人入胜的噩梦和大量的shell注入机会,我建议使用一个模块,例如String :: ShellQuote

Then, I don't see that you need all those external tools, while such a long pipe-line is tricky and expensive. 然后,我看不到您需要所有这些外部工具,而如此长的管道既棘手又昂贵。 It invokes a number of programs, for jobs done really well in Perl, and requires very precise syntax. 它为Perl中确实完成的工作调用了许多程序,并且需要非常精确的语法。

Apart from ssh -ing, one other thing that an external tool may be good for here is to extract lines of interest with grep , in case that file is huge (otherwise you can read it into a scalar). 除了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;

Once it comes to running external commands, there are many options. 一旦运行外部命令,就有许多选择。 In the first place consider using a module. 首先考虑使用模块。 They all simplify things a lot, in particular with error checking, and are in general more reliable, while some also make harder jobs far more manageable. 它们都大大简化了工作,尤其是通过错误检查,并且通常更可靠,而有些还使更困难的工作更易于管理。

An example with IPC::System::Simple IPC :: System :: Simple的示例

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

my @lines = capturex('ssh', $remote_addr, $remote_cmd);

Since ssh executes the command when ran with one it doesn't need a shell (for that part) so capturex is used. 由于ssh在运行时执行命令,因此它不需要外壳(对于该部件),因此使用capturex See documentation for more options and for how to check for errors. 请参阅文档以获取更多选项以及如何检查错误。

Some other options, from simple to more powerful, are Capture::Tiny , IPC::Run3 , IPC::Run . 从简单到更强大的其他一些选项是Capture :: TinyIPC :: Run3IPC :: Run

For more on all this see links assembled in this post (and search for more). 欲了解更多有关这一切看到组装环节这篇文章 (和寻找更多)。


I can't see a need to run that pipeline as it stands but if there is one (stay on the remote host?) then form commands as above and then assemble the full pipeline 我看不到一个需要运行的管道,因为它代表但如果有一个(留在远程主机上的?)然后形成如上命令,然后组装完整的管道

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";

Note that the needed shell functionalities ( | and redirection) shouldn't be quoted away. 请注意,不应引用所需的shell功能( |和重定向)。

The mere space in the awk piece may be awkward-looking but since it gets escaped it winds up right for -F . awk块中仅有的空间可能看起来很尴尬,但是由于它逃脱了,所以它会朝-F For me it's another sign of trouble with running external programs in shell pipelines; 对我来说,这是在shell管道中运行外部程序的另一个麻烦迹象。 I couldn't figure out that bare space, thanks to Charles Duffy for the comment. 多亏了查尔斯·达菲Charles Duffy)的评论,我无法弄清那空余的空间。

In this case the sort and uniq parts of the pipeline can be just typed as one string since it's only program names and options, but as soon as changes are made or any variables make their way in that becomes tricky. 在这种情况下,流水线的sortuniq部分只能作为一个字符串键入,因为它只是程序名称和选项,但是一旦进行更改或任何变量进入其中,就变得很棘手。 So I use shell_quote , for consistency and as a template. 因此,我使用shell_quote来保持一致性并用作模板。


Modules are reported missing and hard to obtain. 据报告模块丢失,难以获得。 Then escape what need be escaped (until you figure out how to get the modules, that is). 然后逃避需要逃避的内容(直到您弄清楚如何获取模块)。 In this case there happen to be little to fix, but that bit can serve as an example of common hoops to go through with complicated pipelines. 在这种情况下,几乎没有什么要修复的,但是该位可以作为常见的复杂循环流水线的示例。

The string with $text needs to reach grep as such, one string. 带有$text的字符串需要到达grep这样的一个字符串。 Since it passes through shell, which would break it by space into words, we need to protect (quote/escape) those spaces. 由于它通过外壳,它将被空间分解成单词,因此我们需要保护(引用/转义)那些空间。 Not to forget, we also need to get it to the shell in the first place, through Perl's parsing rules. 别忘了,我们还需要首先通过Perl的解析规则将其放入外壳。

One way 单程

my $text_quoted = q(') . quotemeta($text) . q(');

where quotemeta quotes all kinds of other things as well. quotemeta还会引用其他所有内容。

We also should protect the filename pattern, as it may rely on shell metacharacters (like * ) 我们还应该保护文件名模式,因为它可能依赖于shell元字符(例如*

my $remote_path_quoted = quotemeta $remote_path;

but again, you have to inspect whether this is suitable in each case. 但同样,您必须检查每种情况是否合适。

NOTE If any dynamically generated variables (computed, come from user...) are interpolated in these commands they need be validated, with things carefully escaped and quoted . 注意如果在这些命令中插入了任何动态生成的变量(计算所得,来自用户...),则需要对它们进行验证,并仔细进行转义和引用

Now your pipeline should work (it does in my simulated tests) 现在您的管道应该可以工作了(在我的模拟测试中可以)

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);

This can be broken up into sensible components in their own variables, etc, but I really don't recommend such hand-patched solutions. 可以将它们分解为自己变量中的明智组件,等等,但是我真的不建议您使用此类手动修补的解决方案。

I suggest to only use the first part (ssh + grep) and do the rest in Perl though, as in the main part of the answer. 我建议只使用第一部分(ssh + grep),其余部分在Perl中进行,就像答案的主要部分一样。 And then to have those modules installed and switch to them. 然后安装这些模块并切换到它们。

No major computing tool works without (the many) libraries, and every production install contains a lot of "additional" stuff. 没有(很多)库,没有任何主要的计算工具可以工作,并且每个生产安装都包含许多“附加”内容。 As need for more libraries arises they are installed. 随着对更多库的需求的出现,它们被安装。 Why would it have to be different with Perl? 为什么与Perl必须有所不同? Yes, you can do it with builtins only but that may be much harder. 是的,您只能使用内建函数来执行此操作,但这可能要困难得多。


One good reason would be so to use the system sort when files are huge, since it doesn't have to load the whole file at once, and for its speed. 一个很好的原因是在文件很大时使用系统sort ,因为它不必一次加载整个文件,而且不必因为速度而加载整个文件。 However, in this pipeline it is fed data over the pipe and invoked repeatedly so these advantages don't apply. 但是,在此管道中,它通过管道馈送数据并重复调用,因此这些优点不适用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM