如何在正则表达式中使用awk变量?

时间:2012-07-18 04:09:44

标签: regex awk

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

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

我还有另一个名为网站的文件,其中包含一些网站网址和数字。例如:

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

现在我要计算每个域名的网址数量。例如: google.com 10 + 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

然后,因为我刚开始编写脚本

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

5 个答案:

答案 0 :(得分:30)

如果您不使用awk正则表达式标记,

//可以与变量匹配。

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

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

regex = dom"$"

然后匹配regex变量

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

答案 1 :(得分:18)

首先,变量为dom而非$dom - 将$视为运算符,以提取存储在变量dom <中的列号的值/ p>

其次,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 

答案 2 :(得分:1)

使用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

答案 3 :(得分:1)

您显然希望一次阅读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

答案 4 :(得分:0)

上述答案的问题是,如果您使用字符串而不是正则表达式/.../,则不能使用“元字符”(例如\ <表示单词开头的单词边界)。 如果您有一个域xyz.com以及两个站点ab.xyz.com和cd.prefix_xyz.com,则两个站点条目的编号将被添加到xyz.com

这是使用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
}

...