【问题标题】:BASH: Search a string and exactly display the exact number of times a substring happens inside itBASH:搜索字符串并准确显示子字符串在其中出现的确切次数
【发布时间】:2018-10-07 17:59:47
【问题描述】:

我已经搜索了所有内容,但仍然找不到这个简单的答案。我敢肯定它很容易。如果您知道如何完成此操作,请提供帮助。

sample.txt 是:

AAAAA

我想找出组合“AAA”发生的确切时间。如果你只是使用例如

grep -o 'AAA' sample.txt | wc -l

我们收到 1。这与使用标准文本编辑器搜索框类型搜索仅搜索 AAA 发生的次数相同。但是,我想要完全匹配的完整数量,从每个恰好为 3 的单个字符开始。当我们从每个字符单独搜索而不是将每个 AAA 命中视为盒子类型块时,我们会得到这个。

我正在寻找从 sample.txt 中“AAA”的每个单独字符开始的最压缩/最多可能性/字面确切的出现次数,而不仅仅是像在普通文本中那样找到它的每次块从搜索框中编辑器类型搜索。

我们如何做到这一点,最好是在 AWK 中? SED、GREP 和其他任何东西都可以,我可以将它们包含在 Bash 脚本中。

【问题讨论】:

  • 我认为这不能回答 OP 的问题。他们正在寻找组合。在示例中,有三种方法可以从AAAAA 中获取AAA。这实际上在 bash 中有点棘手......
  • 完全正确!!感谢任何 Novy,该页面建议使用 sed 's/echo/echo\n/g' 文件 | grep -c "echo" 这不完全是我的情况,因为我需要从字符串中每个字符的开头开始计数,以便像约翰所说的那样获得每一种可能性,我需要它来识别 AAAAA 中的 3 个 AAA。我们通过从每个字符的开头特别计数来得到这个,但是如何在 AWK 或 grep 中做到这一点对我来说很难与谷歌相遇。我吃了 GREP 手册页,所有的特殊运算符仍然找不到正确的代码。

标签: regex bash search awk sed


【解决方案1】:

我在 OP 的另一个帖子上发布了这个,但它被忽略了,可能是因为我没有添加注释和解释。只是一种不同的方法,欢迎任何讨论。

$ awk -v sample="$(<sample.txt)" '{ x=sample; n=0 }$0 != ""{
    while(t=index(x,$0)){ n++; x=substr(x,t+1) } 
    print $0,n
}' combinations

说明:

变量:

  • sample: 是带有 -v 参数的文件 sample.txt 中的原始示例文本 slurp
  • x:为目标字符串,每次测试前,将值重置为sample
  • $0: 是来自文件combination 的测试字符串,每行输入一个测试字符串
  • n: 是计数器,测试字符串的出现次数($0)
  • t: 是匹配的测试字符串($0)的第一个字符在目标字符串(x)中的位置

更新:在主 while 循环之前添加了 $0 != "" 以跳过导致无限循环的 EMPTY 字符串。

代码:

    awk -v sample="$(<sample.txt)"   '

        # reset the targeting string(with the sample text) and the counter "n" 
        { x = sample; n = 0 }  

        # below the main block where $0 != "" to skip the EMPTY testing string
        ($0 != ""){
            # the function index(x, $0) returns the position(assigned to "t") of the first character 
            # of the matched testing string($0) in the targeting string(x). 
            # when no match is found, it returns zero and thus step out of the while loop.
            while(t=index(x,$0)) {
                n++;                # increment the number of matches 
                x = substr(x, t+1)  # modify the targeting string to remove all characters before the position(t) inclusively 
            }
            print $0, n             # print the testing string and the counts 
        }
    ' combinations

awk index() 是一个比正则表达式匹配快得多的函数,它不需要以暴力方式进行昂贵的字符串比较。附上测试过的sample.txt和组合:

$ more sample.txt 
AAAAAHHHAAHH
HAAAAHHHAAHH
AAHH

$ more combinations 
AA
HH
AAA
HHH
AAH
HHA
ZK

测试环境:GNU Awk 4.0.2、Centos 7.3

【讨论】:

    【解决方案2】:

    这可能对你有用(GNU sed 和 wc):

    sed -r 's/^[^A]*(AA?[^A]+)*AAA/AAA\nAA/;/^AAA/P;D' | wc -l
    

    丢失除A's 和单个或两个A's 以外的任何字符。然后打印三个A 并丢失第一个A 并重复。最后统计一下打印的行数。

    【讨论】:

      【解决方案3】:

      这是 awk 版本

      echo "AAAAA AAA AAAABBAAA"  \
      | gawk -v pat="AAA" '{ 
          for(i=1; i<=NF; i++){
              # current field length
              m=length($i)
              #search pattern length
              n=length(pat)
              for(l=1 ; l<m; l++){
                  sstr=substr($i,l,n)
                  #print i " " $i " sub:" sstr
      
                  # substring matches pattern
                  if(sstr ~ pat){
                      count++
                  }else{
                      print "contiguous count on field " i " = " count
                      # uncomment next line if non-contiguous matches are not needed
                      #break
                  }
              }
              print "total count on field " i " = " count
              count=0
          }
      
      }'
      

      【讨论】:

      • 这只是计算输入中包含多少大小为 3 的字符串。它实际上并不寻找搜索词。例如,对于输入 AAAAA AAA,它应该输出 4 时会输出 7。
      • 正确,但这不是 OP 所要求的。
      【解决方案4】:

      这不是 bash 中的小问题。据我所知,标准实用程序不支持这种搜索。但是,您可以使用标准 bash 功能在函数中实现此行为。以下是我解决问题的方法,但还有其他方法:

      #!/bin/bash
      
      search_term="AAA"
      text=$(cat sample.txt)
      term_len=${#search_term}
      occurences=0
      
      # While the text is greater than or equal to the search term length
      while [ "${#text}" -ge "$term_len" ]; do
      
          # Look at just the length of the search term
          text_substr=${text:0:${term_len}}
      
          # If we see the search term, increment occurences
          if [ "$text_substr" = "$search_term" ]; then
              ((occurences++))
          fi
      
          # Remove the first character from the main text
          # (e.g. "AAAAA" becomes "AAAA")
          text=${text:1}
      done
      
      printf "%d occurences of %s\n" "$occurences" "$search_term"
      

      【讨论】:

      • 我相信您也可以在 awk 中执行此操作。我个人对 awk 不太熟悉,但除了控制流之外,上述脚本所做的只是计算字符串中的字符数,生成子字符串,并从字符串中删除第一个字符。所有这些都可以在 awk 中完成(尽管我会留给您研究;)。
      • 在我的回答中查看 awk 实现。
      猜你喜欢
      • 2023-03-13
      • 2017-06-16
      • 1970-01-01
      • 2012-07-13
      • 2015-12-25
      • 2018-06-10
      • 2020-02-01
      • 2020-02-27
      • 1970-01-01
      相关资源
      最近更新 更多