【问题标题】:How to use awk variables in regular expressions?如何在正则表达式中使用 awk 变量?
【发布时间】:2012-07-17 01:31:30
【问题描述】:

我有一个名为 domain 的文件,其中包含一些域。例如:

google.com
facebook.com
...
yahoo.com

我还有另一个名为 site 的文件,其中包含一些网站的 URL 和编号。例如:

image.google.com   10
map.google.com     8
...
photo.facebook.com  22
game.facebook.com   15
..

现在我要计算每个域的 url 编号。例如:google.com10+8。所以我写了一个这样的awk脚本:

BEGIN{
  while(getline dom < "./domain" > 0) {
    domain[dom]=0;
  }
  for(dom in domain) {
    while(getline < "./site" > 0) {
      if($1 ~/$dom$)   #if $1 end with $dom {
        domain[dom]+=$2;
      }
    }
  }
}

但是代码if($1 ~/$dom$) 并没有像我想要的那样运行。因为正则表达式中的变量 $dom 是按字面解释的。所以,第一个问题是:

有没有办法在正则表达式中使用变量$dom

那么,由于我是编写脚本的新手

有没有更好的方法来解决我遇到的问题?

【问题讨论】:

    标签: regex awk


    【解决方案1】:

    如果您不使用// 正则表达式标记,awk 可以匹配变量。

    if ( $0 ~ regex ){ print $0; }

    在这种情况下,将所需的正则表达式构建为字符串

    regex = dom"$"
    

    然后匹配regex变量

    if ( $1 ~ regex ) {
      domain[dom]+=$2;
    }
    

    【讨论】:

      【解决方案2】:

      首先,变量是dom而不是$dom——考虑$作为一个运算符来提取存储在变量dom中的列号的值

      其次,awk 不会插入 // 之间的内容——那只是一个字符串。

      您想要 match() 函数,其中第二个参数可以是被视为正则表达式的字符串:

      if (match($1, dom "$")) {...}
      

      我会编写如下解决方案:

      awk '
        FNR == NR {domain[$1] = 0; next}
        {
          for (dom in domain) {
            if (match($1, dom "$")) {
              domain[dom] += $2
              break
            }
          }
        }
        END {for (dom in domain) {print dom, domain[dom]}}
      ' domain site 
      

      【讨论】:

      • 对于所有谈论变量如何不以 $ 为前缀的人来说,最好解释一下(IMO),因为 $ 表示 awk 中的字段,所以无论 dom 的值是什么,$dom 都表示字段。 awk 中的变量使用不带引号且不带 $。这不是贝壳!
      【解决方案3】:

      使用awk 脚本的一种方式:

      BEGIN {
          FS = "[. ]"
          OFS = "."
      }
      
      FNR == NR {
          domain[$1] = $0
          next
      }
      
      FNR < NR {
          if ($2 in domain) {
              for ( i = 2; i < NF; i++ ) {
                  if ($i != "") {
                      line = (line ? line OFS : "") $i
                  }
              }
              total[line] += $NF
              line = ""
          }
      }
      
      END {
          for (i in total) {
              printf "%s\t%s\n", i, total[i]
          }
      }
      

      运行方式:

      awk -f script.awk domain.txt site.txt
      

      结果:

      facebook.com    37
      google.com  18
      

      【讨论】:

      • 如果您在site 文件中获得类似“first.second.example.com”的域,则此方法将不起作用。
      • @glennjackman,是的,你是对的。我没有考虑到:-(
      【解决方案4】:

      您显然想读取一次site 文件,而不是domain 中的每个条目一次。不过,解决这个问题是微不足道的。

      同样,awk 中的变量($0 .. $9 等字段除外)不以 $ 为前缀。特别是,$dom 是由变量 dom 标识的字段编号(通常,这将是 0,因为域字符串不会转换为任何其他数字)。

      我认为您需要找到一种方法来从site 文件中读取的数据中获取域。我不确定您是否需要处理具有国家域的站点,例如 bbc.co.uk 以及 GTLD 中的站点(google.com 等)。假设你不是在处理国家域,你可以使用这个:

      BEGIN {
          while (getline dom < "./domain" > 0) domain[dom] = 0
          FS = "[ .]+"
          while (getline  < "./site" > 0)
          {
              topdom = $(NF-2) "." $(NF-1)
              domain[topdom] += $NF          
          }
          for (dom in domain) print dom "  " domain[dom]
      }
      

      在第二个while循环中,有NF字段; $NF 包含计数,$1 .. $(NF-1) 包含域的组件。所以,topdom 最终包含了顶级域名,然后用于索引到在第一个循环中初始化的数组。

      给定问题中的数据(减去点线),输出为:

      yahoo.com  0
      facebook.com  37
      google.com  18
      

      【讨论】:

        【解决方案5】:

        上述答案的问题是,如果您使用字符串而不是正则表达式 /.../,则不能使用“元字符”(例如,\

        这是一个使用 awk 的管道和 sed 命令的解决方案: ...

        for(dom in domain) {
            while(getline < "./site" > 0) {
                # let sed replaces occurence of the domain at the end of the site
                cmd = "echo '" $1 "' | sed 's/\\<'" dom "'$/NO_VALID_DOM/'"
                cmd | getline x
                close(cmd)
                if (match(x, "NO_VALID_DOM")) { 
                  domain[dom]+=$2;
                }
            }
            close("./site") # this misses in original code
        }
        

        ...

        【讨论】:

          猜你喜欢
          • 2015-05-17
          • 2013-05-28
          • 1970-01-01
          • 1970-01-01
          • 2020-11-14
          • 2011-02-05
          • 1970-01-01
          • 2017-10-11
          相关资源
          最近更新 更多