Shell 从perl脚本运行Unix命令

Shell 从perl脚本运行Unix命令,shell,perl,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

我试图在服务器上执行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脚本中这样做。

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

然后,我不认为您需要所有这些外部工具,而如此长的管道是棘手和昂贵的。它调用了许多程序,用于在Perl中非常出色地完成的工作,并且需要非常精确的语法

除了
ssh
-ing之外,外部工具在这里还可以用来提取感兴趣的行,以防文件太大(否则可以将其读入标量)

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

其他一些选项,从简单到更强大,有

有关所有这些的更多信息,请参阅中组装的链接(并搜索更多)


我看不出有必要按原样运行该管道†但如果有(留在远程主机上?),则按上述方式生成命令,然后组装整个管道

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";
请注意,不应引用所需的shell功能(
|
和重定向)

awk
片段中的空间看起来可能很尴尬,但由于它被转义,它就正好用于
-F
。对我来说,这是在shell管道中运行外部程序时出现问题的另一个迹象;多亏了你的评论,我想不出那个空旷的地方

在这种情况下,管道的
sort
uniq
部分可以作为一个字符串键入,因为它只是程序名和选项,但一旦发生更改或任何变量进入,这就变得很棘手。因此我使用
shell\u quote
,以保持一致性并作为模板


据报告,模块缺失且难以获取。然后转义需要转义的内容(直到您知道如何获取模块为止)。在这种情况下,几乎没有什么需要修复的,但该钻头可以作为普通箍的一个例子,通过复杂的管道

带有
$text
的字符串需要到达
grep
这样的一个字符串。因为它通过shell,shell会将它按空格分隔成单词,所以我们需要保护(引用/转义)这些空格。别忘了,我们还需要首先通过Perl的解析规则将它放到shell中

单程

my $text_quoted = q(') . quotemeta($text) . q(');
这里还引用了各种其他的东西

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

但同样,您必须检查这是否适用于每种情况

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

现在,您的管道应该可以工作了(在我的模拟测试中可以)

这可以分解为各自变量中的合理组件等,但我真的不推荐这种手工修补的解决方案

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

如果没有(许多)库,任何主要的计算工具都无法工作,而且每次生产安装都包含大量“附加”内容。随着对更多库的需求的增加,它们被安装。为什么Perl会有不同?是的,你只能用内置的来做,但那可能要困难得多



† 一个很好的理由是,当文件很大时,可以使用系统
sort
,因为它不必一次加载整个文件,而且速度也很快。但是,在这个管道中,它通过管道传输数据并反复调用,因此这些优点不适用。

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

然后,我不认为您需要所有这些外部工具,而如此长的管道是棘手和昂贵的。它调用了许多程序,用于在Perl中非常出色地完成的工作,并且需要非常精确的语法

除了
ssh
-ing之外,外部工具在这里还可以用来提取感兴趣的行,以防文件太大(否则可以将其读入标量)

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

其他一些选项,从简单到更强大,有

有关所有这些的更多信息,请参阅中组装的链接(并搜索更多)


我看不出有必要按原样运行该管道†但如果有(留在远程主机上?),则按上述方式生成命令,然后组装整个管道

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";
请注意,不应引用所需的shell功能(
|
和重定向)

awk
片段中的空间看起来可能很尴尬,但由于它被转义,它就正好用于
-F
。对我来说,这是在shell管道中运行外部程序时出现问题的另一个迹象;多亏了你的评论,我想不出那个空旷的地方

在这种情况下,管道的
sort
uniq
部分可以作为一个字符串键入,因为它只是程序名和选项,但一旦
my $text_quoted = q(') . quotemeta($text) . q(');
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);