【问题标题】:Grep a string with number greater than 45grep 一个数字大于 45 的字符串
【发布时间】:2020-08-07 08:58:30
【问题描述】:

我在一个目录中有多个文件。我想提取包含整数值大于 45 的所有文件中的每一行。

目前,我正在使用:

grep "IO resumed after" *

它向我显示此字符串“IO 恢复后”的所有文件 我想再添加一个参数,它将 grep 所有行“IO 在 [number >45] 秒后恢复”

【问题讨论】:

  • 欢迎来到 SO,您能否在代码标签中的问题中发布您的 Input_file 示例和预期输出以更清晰。
  • 数字是否(或可以)有小数点?
  • 你接受了我的回答,所以对你来说似乎已经足够了。无论如何,您应该回复 cmets 并使您的问题更清楚。重新阅读问题后,我注意到“整数值大于 45”。你能确认这些数字不包含小数点吗?附加问题:数字可以有前导零,例如0045 而不是 45?

标签: shell unix grep agrep


【解决方案1】:

最好使用awk

awk 'match($0,"IO resumed after") { if (substr($0,RSTART+RLENGTH)+0 > 45) print }' file

这将搜索字符串“IO resumed after”,如果找到该字符串,它将获取该字符串之后的所有内容并将其转换为数字:如果“IO resumed after”之后的子字符串以数字开头,那么它将当我们向它添加零时将其转换为该数字。

只有当行看起来像这样时,这才有效:

xxxxIO resumed after_nnnnyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy

其中xy 是随机字符,下划线是任意空格序列,n 是数字。

您可以使用以下命令集对其进行测试:

$ seq 40 0.5 50 | awk '{print "foo IO resumed after",$0,"random stuff"}' \
  | awk 'match($0,"IO resumed after") { if (substr($0,RSTART+RLENGTH)+0 > 45) print }'

哪个输出:

foo IO resumed after 45.5 random stuff
foo IO resumed after 46.0 random stuff
foo IO resumed after 46.5 random stuff
foo IO resumed after 47.0 random stuff
foo IO resumed after 47.5 random stuff
foo IO resumed after 48.0 random stuff
foo IO resumed after 48.5 random stuff
foo IO resumed after 49.0 random stuff
foo IO resumed after 49.5 random stuff
foo IO resumed after 50.0 random stuff

【讨论】:

    【解决方案2】:

    看起来我需要学习 awk 直到那时我有一个 bash 解决方案。如果秒没有小数点,那么这个:

    while read line; do
        number=${line//*after}
        number=${number//seconds*}
        ((number>45)) && echo $line
    done <<< $(grep "IO resumed after" *)
    

    否则我们必须使用bc:

    while read line; do
        number=${line//*after}
        number=${number//seconds*}
        case $(bc <<< "$number>45") in 1) echo "$line";; esac
    done <<< $(grep "IO resumed after" *)
    

    【讨论】:

      【解决方案3】:

      您可以使用替代和重复计数来定义大于 45 的数字的搜索模式。

      此解决方案假定数字是不带小数点的整数。

      grep 'IO resumed after \(4[6-9]\|[5-9][0-9]\|[0-9]\{3,\}\) seconds'
      

      或者更短的egrep:

      egrep 'IO resumed after (4[6-9]|[5-9][0-9]|[0-9]{3,}) seconds'
      

      我用

      测试了这个模式
      for i in 1 10 30 44 45 46 47 48 49 50 51 60 99 100 1234567
      do
      echo "foo IO resumed after $i seconds bar"
      done | grep 'IO resumed after \(4[6-9]\|[5-9][0-9]\|[0-9]\{3,\}\) seconds'
      

      打印出来的

      foo IO resumed after 46 seconds bar
      foo IO resumed after 47 seconds bar
      foo IO resumed after 48 seconds bar
      foo IO resumed after 49 seconds bar
      foo IO resumed after 50 seconds bar
      foo IO resumed after 51 seconds bar
      foo IO resumed after 60 seconds bar
      foo IO resumed after 99 seconds bar
      foo IO resumed after 100 seconds bar
      foo IO resumed after 1234567 seconds bar
      

      如果数字(可以)有小数点,则很难为数字 > 45 定义模式,例如45.1.
      此模式允许小数点或逗号后跟数字并实现条件 >= 46。

      grep 'IO resumed after \(4[6-9]\|[5-9][0-9]\|[0-9]\{3,\}\)\([.,][0-9]*\)\{,1\} seconds'
      

      第二次编辑:

      上面的模式不处理可能的前导零。正如用户kvantour 在评论中所建议的那样,可以扩展该模式以处理此问题。此外,如果不需要检查seconds 部分,则可以省略小数的模式。

      数字 >= 45 的模式,带有可选的前导零:

      grep 'IO resumed after 0*\(4[5-9]\|[5-9][0-9]\|[1-9][0-9]\{2,\}\)'
      

      【讨论】:

      • @MarkSetchell 如果我们将问题从大于 45 更改为大于或等于 45,如果从 grep 中删除字符串 " seconds",则小数点无关紧要。
      • 请注意,正则表达式 [0-9]\{3,\} 也将匹配 000 和所有低于 45 的数字。因此最好强制第一个数字大于 0 并将其替换为。 [1-9][0-9]\{2,\} 。此外,如果您在字符串中添加任何零序列,您将成为防弹:0*\(4[5-9]\|[5-9][0-9]\|[1-9][0-9]\{2,\}\)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-27
      • 1970-01-01
      • 2013-03-11
      • 2018-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多