代码之家  ›  专栏  ›  技术社区  ›  Developer

从perl脚本运行Unix命令

  •  0
  • Developer  · 技术社区  · 7 年前

    我尝试在服务器上执行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
    

    1 回复  |  直到 7 年前
        1
  •  6
  •   zdim    7 年前

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

    那么,我不认为你需要所有的外部工具,而这么长的管道是棘手的和昂贵的。它调用了许多程序,用于在Perl中做得非常好的工作,并且需要非常精确的语法。

    除了 ssh 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::System::Simple

    use IPC::System::Simple qw(capturex); 
    
    my @lines = capturex('ssh', $remote_addr, $remote_cmd);
    

    宋承宪 capturex 已使用。有关更多选项以及如何检查错误,请参阅文档。

    其他一些选项,从简单到更强大,包括 Capture::Tiny , IPC::Run3 IPC::Run

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


    我看不出有必要按目前的情况运行管道 但是如果有一个(留在远程主机上?),那么按照上面的方式生成命令,然后组装完整的管道

    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 . 对我来说,这是在shell管道中运行外部程序出现问题的另一个迹象;我想不出那片空旷的地方,多亏了我 Charles Duffy 征求意见。

    在这种情况下 sort uniq shell_quote ,以保持一致性并作为模板。


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

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

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

    没有一个主要的计算工具没有(很多)库就不能工作,而且每个产品安装都包含很多“额外”的东西。随着对更多库的需求的增加,它们被安装了。为什么它与Perl不同?是的,你 可以 只使用内置软件,但这可能要困难得多。


    分类