【问题标题】:grep command to find previous word less than a numbergrep 命令查找小于数字的前一个单词
【发布时间】:2016-09-22 17:44:54
【问题描述】:

我需要一个 grep 命令的帮助:

grep match-word tomcat-0.log.* | grep "TOMCAT BENCH" | grep -v Normal

当前输出类似于:

tomcat-0.log:TOMCAT BENCH: match_word random-text 1420 elapsed Thu 2016-09-22 06:31:04:928 PDT <SessionID: id> <RequestID: reqId> 

我想修改它以仅显示经过值大于3000等数字的那些行

单词elapsed 始终存在,数字是单词before elapsed

您能否修改grep 命令以过滤前一个单词并将其与数字进行比较?

【问题讨论】:

  • 我认为awk 更适合这种任务......
  • 虽然awk 解决方案更干净,但如果您有兴趣,我给您提供了grep 解决方案。 :)
  • 我无法使用 awk,因为日志被压缩了,我实际上是先使用 zgrep 解压缩它
  • 只需使用zcat file | awk。当您需要不同的阈值时,其他任何修改都会变得异常复杂。
  • @amitmah:您应该提到过有问题的压缩文件。同样正如@Ed 正确建议您使用zcatawk 一样,使用多个grep 不仅效率低下,而且不是进行数值比较的正确工具。

标签: regex linux shell awk grep


【解决方案1】:

使用 gnu awk 可以在一个命令中完成:

awk '/TOMCAT BENCH/ && !/Normal/ && match($0, / ([0-9]+) elapsed /, a) && a[1] > 3000' tomcat-0.log.*

【讨论】:

  • 使用 POSIX awk 可以做到:awk '/TOMCAT BENCH/ &amp;&amp; !/Normal/ &amp;&amp; match($0, / ([0-9]+) elapsed /) &amp;&amp; (p=substr($0, RSTART+1, RLENGTH-1)) != "" &amp;&amp; sub(/ .*/, "", p) &amp;&amp; p &gt; 3000' log.txt
  • POSIXly 我有awk '/TOMCAT BENCH/ &amp;&amp; !/Normal/{s=$0; $0=substr($0,1,index($0,"elapsed"))} $(NF-1) &gt; 3000{print s}' 但它的想法相同,所以我删除了我的答案。
【解决方案2】:

保持简单,您只需要:

zcat file |
awk -F ' *elapsed.*' '/TOMCAT BENCH/ && !/Normal/{n=$1;sub(/.* /,"",n)} n>3000'

例如使用您发布的一行示例输入:

$ cat file |
awk -F ' *elapsed.*' '/TOMCAT BENCH/ && !/Normal/{n=$1;sub(/.* /,"",n)} n>1400'
tomcat-0.log:TOMCAT BENCH: match_word random-text 1420 elapsed Thu 2016-09-22 06:31:04:928 PDT <SessionID: id> <RequestID: reqId>

【讨论】:

  • 感谢这样更容易更改时间:)
【解决方案3】:

根据要求,这是一个全是 grep 的解决方案,尽显蛮力荣耀:

... | grep -E "([1-9][0-9]{4,}|3[0-9]{2}[1-9]|3[0-9][1-9]0|[4-9][0-9]{3}) elapsed"

让我们来解决这个问题:

  • [1-9][0-9]{4,} 匹配任何大于 9999 的数字。基本上,它会验证我们是否有一个数字字符串,其中包含大于 0 的 ten thousands place, 100 thousands place, ... 或更高的数字。
    例如,12000会匹配,但02000 不会匹配。
  • 3[0-9]{2}[1-9] 匹配所有以零结尾的数字3001 - 3999
  • 3[0-9][1-9]0 匹配像 3010, 3120, 3990, etc. 这样以 0 结尾但不小于或等于 3000 的数字
  • [4-9][0-9]{3} 匹配大于 3999 的 4 位数字

如果前面的模式之一匹配,我们确保它后面紧跟字符串“elapsed”,在这种情况下,我们就完成了。


PS:记住,我们必须匹配大于 3000的数字。

PPS:请注意,我假设“elapsed”之前的字符串总是由数字组成;在我们检查数字之前,我不确定是否有空格。

PPPS:这是通过grep 完成的,因为它是被要求提供解决方案的工具。我绝不是建议 grep 是这样做的好方法...根本没有。

PPPPS:由于正在搜索的日志格式,我预计不必处理负数。因此,我没有。 :)

【讨论】:

  • 那个正则表达式比它所需要的复杂得多(它相当于[3-9][0-9]{3})但更重要的是它无法匹配10000、2000等。你可以纠正那个到([3-9]|[1-9][0-9])[0-9]{3},但它仍然是进行数字比较的错误方法。只需使用 awk。
  • @EdMorton:感谢您的反馈!但是,我认为您在几点上是错误的。首先,它等同于[3-9][0-9]{3},因为它匹配3000,我们不应该这样做! (注意:出于某种疯狂的原因,即使我不应该匹配,我也分别匹配了 3000。但是,上面 部分 更正的解决方案不再与 @ 相同987654339@。)其次,它不应该与2000匹配。第三,OP要求grep解决方案,后来又说他不能使用awk。因此,在更新它以允许更大的数字后,我会坚持我的答案。 :D
  • @EdMorton:在那里。我给了一些读者注释,它现在可以处理大于 9999 的数字。JSYK。
  • 您拥有的正则表达式绝对等同于我在评论中发布的内容,并且正如您在评论中所说的那样,它确实匹配 3000,当然我的意思是 20000,而不是我评论中的 2000。我理解您为什么发布 grep 解决方案,OP 说他不能使用 awk 是完全错误的,恕我直言,可以从正确的方向受益,而不仅仅是给他他所要求的。
  • @EdMorton:事实上,我同意你的看法。正如你所看到的,我确实澄清了grep 不是这样做的好方法这一事实,我个人更喜欢awk 解决方案,因为它们更干净、更优雅、更灵活。 (说实话,我已经 +!d 了几个awk 解决方案,因为它们是最好的方法。)即使我对我的帖子有点激进(我承认 - 我在吹毛求疵,所以我有更多点),如果我知道它的存在,我可能会说一些关于zcat 的事情。无论如何,感谢您的 cmets - 他们让我注意到了 2 个错误(现已修复)。问候。
【解决方案4】:

您可以使用awk命令如下:

awk '{for(i=1;i<=NF;i++){ if($i == "elapsed") { if ($(i-1) >3000 ) print; } }}' file

假设您的示例输入文件是

$ cat file
t-0.log:TOMCAT BENCH: match_word random-text 1420 elapsed Thu 2016-09-22 06:31:04:928 PDT <SessionID: id> <RequestID: reqId> 
t-0.log:TOMCAT BENCH: match_word random-text 5420 elapsed Thu 2016-09-22 06:31:04:928 PDT <SessionID: id> <RequestID: reqId> 
t-0.log:TOMCAT BENCH: match_word random-text 420  elapsed Thu 2016-09-22 06:31:04:928 PDT <SessionID: id> <RequestID: reqId> 
t-0.log:TOMCAT BENCH: match_word random-text 3100 elapsed Thu 2016-09-22 06:31:04:928 PDT <SessionID: id> <RequestID: reqId> 
t-0.log:TOMCAT BENCH: match_word random-text  0 elapsed Thu 2016-09-22 06:31:04:928 PDT <SessionID: id> <RequestID: reqId> 
t-0.log:TOMCAT BENCH: match_word random-text 6596 elapsed Thu 2016-09-22 06:31:04:928 PDT <SessionID: id> <RequestID: reqId>

运行awk 命令产生

$ awk '{for(i=1;i<=NF;i++){ if($i == "elapsed") { if ($(i-1) >3000 ) print; } }}' file
t-0.log:TOMCAT BENCH: match_word random-text 5420 elapsed Thu 2016-09-22 06:31:04:928 PDT <SessionID: id> <RequestID: reqId> 
t-0.log:TOMCAT BENCH: match_word random-text 3100 elapsed Thu 2016-09-22 06:31:04:928 PDT <SessionID: id> <RequestID: reqId> 
t-0.log:TOMCAT BENCH: match_word random-text 6596 elapsed Thu 2016-09-22 06:31:04:928 PDT <SessionID: id> <RequestID: reqId>

【讨论】:

    【解决方案5】:

    你只需要添加

    | awk 'match($0, / ([0-9]+) elapsed /, a) && a[1] > 3000'
    

    在你的命令末尾:

    grep match_word tomcat-0.log.* | grep "TOMCAT BENCH" | grep -v Normal | awk 'match($0, / ([0-9]+) elapsed /, a) && a[1] > 3000'
    

    【讨论】:

    • 不,实际上根据我的问题,中间有一个随机文本,所以我无法预测列号
    • 只是为了修正我的答案,这个命令在任何情况下都可以正常工作。但当然最好的解决方案是由 anubhava 给出的。
    • 您的解决方案是 gawk 特定的,因为 match() 的第三个 arg 所以可能它对 OP 失败,因为他没有使用 gawk。
    【解决方案6】:

    使用numgrep

    ... | grep elapsed | numgrep /3000../
    

    【讨论】:

      猜你喜欢
      • 2019-04-14
      • 1970-01-01
      • 2016-07-16
      • 1970-01-01
      • 2011-08-07
      • 2021-03-03
      • 1970-01-01
      • 1970-01-01
      • 2017-01-18
      相关资源
      最近更新 更多