【问题标题】:Optimize grep, awk and sed shell stuff优化 grep、awk 和 sed shell 的东西
【发布时间】:2010-06-01 11:42:33
【问题描述】:

我尝试对来自“IPCop”的日志文件中不同端口的流量求和,因此我为我的 shell 编写和命令,但我认为可以优化命令。

我的日志文件中的第一行:

01/00:03:16 kernel INPUT IN=eth1 OUT= MAC=xxx SRC=xxx DST=xxx LEN=40 TOS=0x00 PREC=0x00 TTL=98 ID=256 PROTO=TCP SPT=47438 DPT=1433 WINDOW=16384 RES=0x00 SYN URGP=0 

现在我使用以下命令 grep 包含端口 1433 的所有长度的总和

grep 1433 log.dat|awk '{for(i=1;i<=10;i++)if($i ~ /LEN/)print $i};'|sed 's/LEN=//g;'|awk '{sum+=$1}END{print sum}'

我需要 for 循环,因为 LEN-col 始终不在同一位置。

对优化这个命令有什么建议吗?

问候 雷内

【问题讨论】:

    标签: linux shell sed awk grep


    【解决方案1】:

    由于我没有代表对 Noufal Ibrahims 的回答添加评论,这里是使用 Perl 的更自然的解决方案。

    perl -ne '$sum += $1 if /LEN=(\d+)/; END { print $sum; }' log.dat
    

    @Noufal 你可以让 perl 完成所有艰苦的工作;)。

    【讨论】:

    • 我不懂这个命令。我尝试对 var $1 求和,但这个 var 仍然没有定义?! PT=1433 也没有过滤器。你能解释一下如何使用你的语法吗?
    • 是的。我知道。它变得难以阅读的速度如此之快,以至于我通常会避免它。不错的sn-p。谢谢。为您 +1,以便您可以尽快发布 cmets。 :)
    • 我喜欢不可读的东西 :-) 但我不明白你的 sn-p :-( -n 开关代表暂时?那么 $1 如何获得 LEN= 旁边的值?跨度>
    • $1 是第一次捕获正则表达式的结果;在这种情况下,它将是 LEN= 旁边的数字。
    • -n 开关使 perl 将您的代码(-e 'code')包装在一个使用输入行的 while 循环中,并将其分配给 $_ 变量。使用 -p 而不是 -n 也会使其打印该行。如果一行需要多个正则表达式,可以通过扩展 if 语句来完成;我对 LEN 使用了直接抓取,因为我认为这是主要的兴趣点。 Perl 非常适合此类任务,但需要一些学习才能有效使用。附言谢谢努法尔:-)
    【解决方案2】:

    如果它真的需要优化,因为它运行得非常慢:你可能应该用更通用的语言重写它。甚至 AWK 也可以,但我建议使用更接近 Perl 或 Java 的东西来作为长期运行的提取器。

    您可以进行的一个更改是,不要使用不必要的 SED 和第二个 AWK 调用,而是将 END 移到第一个 AWK 调用中,并使用 split() 从 LEN=num; 中提取数字;并将其添加到累加器中。像 split($i, x, "=");总和 += x[2]。

    主要问题是你不能写 awk '/LEN=(...)/ { sum += var matching the ... }'。

    【讨论】:

    • 谢谢你的帖子,这就是我想要的。我寻找一种解决方案来保存第二个 awk 和 sed 命令。但我不知道怎么做。
    • 大多数类似的东西都可以在 AWK 手册中找到。我实际上学会了它,因为 Perl “复制”了这个想法,而且我知道那个 lang 就像我的后脑勺一样,呵呵。
    • 现在我将 Shellcommand 更改为以下命令: grep 'PT=1433' log.dat|awk '{for(i=1;i
    • sub() 执行替换,有点像 sed s///。使用 split() 和数组索引可能是最好的,除非性能是这样的开销,以至于通过索引操作子字符串更有效(以避免正则表达式的成本)。如果性能如此关键,恕我直言,使用自定义 C 或 ASM 应用程序可能是最好的。
    • 我有一个更好的解决方案,运行时间为 0.199 秒 grep 'PT=1433' log.dat|awk '{for(i=1;i
    【解决方案3】:

    只要您在管道中有 grep/sed/awk 组合,您就可以简化为单个 awk 或 perl 命令。这是一个 awk 解决方案:

    gawk -v dpt=1433 '
        $0 ~ dpt {
            for (i=1; i<=NF; i++) {
                if ($i ~ /^LEN=[[:digit:]]+/) {
                    split($i, ary, /=/)
                    sum += ary[2]
                    next
                }
            } 
        } 
        END {print sum}
    ' log.dat
    

    【讨论】:

    • 嘿格伦,谢谢你的 sn-p,但它的速度很慢。使用我的 65MB 日志文件进行测试需要 4.122 秒,并且 sn-p 无法扩展到具有更好性能的更大文件。
    【解决方案4】:

    如果你使用gawk,你可以使用\&lt;来避免for循环的需要,match(-)函数来查找子字符串“\”,即突出你想要的字段,和 substr 来投影 LEN 的参数。然后,您可以只使用单个 awk 调用来执行所有操作。

    后记

    我上面给出的正则表达式不起作用,因为 = 字符不是单词的一部分。以下 awk 脚本确实有效:

    /1433/ { f=match($0,/ LEN=[[:digit:]]+ /); v=substr($0,RSTART+5,RLENGTH-6); s+=v; } END { 打印 "sum=" s; }

    【讨论】:

    • 对不起,我没看懂 gawk 的语法,你能解释一下吗?我尝试 grep 'PT=1433' |gawk '"\"' 这是查找字符串的表达式,但是如何对 gawk 命令中的字段求和?
    • 在我找到的文档中: \
    • 嘿查尔斯,我用你的方式搜索解决方案,但我仍然找不到用 \
    • Hey Charles thx 为您的解决方案,但它比我的第一个 shellcommand 慢得多。此命令需要 4.1 秒,第一个解决方案只需要 0.28 秒,但是当我将命令拆分为: grep 'PT=1433' mai_kernel_log.dat| awk '{f=match($0,/ LEN=[[:digit:]]+ /); v=substr($0,RSTART+5,RLENGTH-6); s+=v;}END{print s;}' 我需要 0.367s
    • @kockiren:你是如何安排脚本的?
    【解决方案5】:

    如果这些将在一行上,您可以使用 perl 提取 LOG 数字并将其相加。

    perl -e '$f = 0; while (<>) {/.*LEN=([0-9]+).*/ ; $f += $1;} print "$f\n";' input.log
    

    我为糟糕的 Perl 道歉。我根本不是 Perl 人。

    【讨论】:

    • 我把你的脚本改成:>perl -e '$f = 0; while () {if(/PT=1433/){/LEN=([0-9]+)/ ; $f += $1;}} 打印 "$f\n";' log.dat 现在我得到了正确的结果。随着 |time 我得到 0.08 秒的差异
    • 所以我尝试使用 perl 和 shell 命令进行测试,如果 I/O 性能足够快,我发现 perl 的编译时间比 shellcommand 的运行时慢。如果日志文件大小为 7GB,则 perlcommand 将捕获到 shellcommand。所以我认为最好优化shellcommand。
    • 您是说对于较小的文件,perl 命令速度较慢,并且只有在接近 7GB 时才能看到收益?我对这个结果感到非常惊讶,因为两个命令管道至少应该遍历文件两次。
    • 我在具有快速 SCSI 设备的同一台机器上尝试使用booth 命令(文件大小为65MB),shellcommand 需要0.0287s 而perl 命令需要0.822s 在普通PC 上的相同测试对于perl 更好,我认为这是因为服务器机器上的快速 I/O 设备显示了 perl 的编译时间,并且编译了 awk、sed、grep 命令,这只是脚本的真正运行时。
    • shellscript 在运行时中有一个 0 到多个。 shellcommand 需要 0.287s :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-28
    • 1970-01-01
    • 1970-01-01
    • 2012-09-05
    • 2022-11-30
    相关资源
    最近更新 更多