40

我有一个名为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在正则表达式中使用变量?

然后,因为我是新来写脚本

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

4

5 回答 5

49

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

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

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

regex = dom"$"

然后匹配regex变量

if ( $1 ~ regex ) {
  domain[dom]+=$2;
}
于 2014-01-31T11:56:02.627 回答
21

首先,变量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 
于 2012-07-18T04:30:52.937 回答
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
于 2012-07-18T11:29:36.903 回答
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
于 2012-07-18T05:53:54.270 回答
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
}

...

于 2019-08-14T08:24:17.563 回答