Chinaunix

标题: 求助:用awk统计站点出现次数 [打印本页]

作者: aximofu    时间: 2010-05-18 13:13
标题: 求助:用awk统计站点出现次数
例:给出一个文件,domain.txt,内容如下:
sina.com
baidu.com
sohu.com
google.com
另外有一个文件,sites.txt
www.sina.com
news.sohu.com
dl1.baidu.com
map.google.cn
www.baidu.com
sports.sina.com
finances.sohu.com
想请问下各位,如何用awk统计出每个域在sites.txt中出现的次数
作者: where27    时间: 2010-05-18 13:25
本帖最后由 where27 于 2010-05-18 13:48 编辑

回复 1# aximofu
  1. awk -F. '{a[$2"."$3]++}END{for(i in a)print i ,a[i]}' sites.txt
复制代码
我好像弄错了,这个是统计sites.txt中各个域出现的次数,这个是应该不是楼主要的,跳过吧
作者: expert1    时间: 2010-05-18 13:26
grep sohu|wc -l
作者: where27    时间: 2010-05-18 13:47
  1. awk 'NR==FNR{a[$0]=0;next}{for(i in a)if($0~i)a[i]++}END{for(i in a)print i,a[i]}' domain.txt sites.txt
复制代码

作者: 99超人    时间: 2010-05-18 14:16
提示: 作者被禁止或删除 内容自动屏蔽
作者: expert1    时间: 2010-05-18 14:19
本帖最后由 expert1 于 2010-05-18 14:27 编辑

awk 'NR==FNR{a[$0]}NR>FNR{for(i in a){if(match($0,i)==1)a++}}END{for(i in a){print i,a}}' domain sites

知道这个不对,因为match函数有问题,sohu.com和www.sohu.com匹配的时候这个if的match如何写啊高手们?

红色部分是a++,为什么发上来了是a++?
作者: bbgg1983    时间: 2010-05-18 14:23
while read line;do echo "$line:"$(grep -c $line sites.txt);done <domain.txt

字符少了不让发
作者: 99超人    时间: 2010-05-18 14:30
提示: 作者被禁止或删除 内容自动屏蔽
作者: expert1    时间: 2010-05-18 14:30
awk 'NR==FNR{a[$0]}NR>FNR{for(i in a){if($0~i)a[i]++}}END{for(i in a){print i,a[i]}}' domain sites

这个没问题,if里如用match来做呢?高手告诉我啊
作者: aximofu    时间: 2010-05-18 14:33
回复 3# expert1


您这个可能不成,我是要从domain.txt那个文本中读取域名的,不是只有sohu
作者: springwind426    时间: 2010-05-18 14:33

  1. awk -F'.' 'NR==FNR{a[$(NF-1) "." $NF]++}NR!=FNR&&($0 in a){print $0 "\t" a[$0]}' sites.txt domain.txt

  2. 分析发现,只需要提取域名的最后两个域,如果domain.txt 中的域名不是2个字段,这段代码不适用

复制代码

作者: where27    时间: 2010-05-18 14:34
回复 5# 99超人
  1. awk -F. 'NR==FNR{a[$0]=0;next}{t=$(NF-1)"."$NF;if(t in a)a[t]++}END{for(i in a)print i,a[i]}' domain.txt sites.txt
复制代码
试试这个
作者: where27    时间: 2010-05-18 14:40
回复 11# springwind426


    呵呵,额也是这么想的,要不是两个字段的话,还真不好办
作者: changying0521    时间: 2010-05-18 14:40
#!/bin/sh
for i  in `cat domain.txt`
do
NO=`grep $i sites.txt|wc -l`
echo $i : $NO >> file
done
可以实现
作者: 99超人    时间: 2010-05-18 14:41
提示: 作者被禁止或删除 内容自动屏蔽
作者: expert1    时间: 2010-05-18 14:50
精确匹配sina.sohu等应该可以了吧?
awk 'NR==FNR{a[$0]}NR>FNR{for(i in a){if($0~i)a[i]++}}END{for(i in a){print i,a[i]}}' domain sites

这个应该可以了。

PS:这样的环境能用FS吗?
awk 'NR==FNR{a[$0]}NR>FNR{FS="."}{for(i in a){if(match($2,i)==1)a[i]++}}END{for(i in a){print i,a[i]}}' domain sites
结果为空,晕倒!!!
作者: aximofu    时间: 2010-05-18 14:52
#!/bin/sh
for i  in `cat domain.txt`
do
NO=`grep $i sites.txt|wc -l`
echo $i : $NO >> file
don ...
changying0521 发表于 2010-05-18 14:40



   用for的方式挺省力,但是有个问题,我的domain.txt可能有1kw条,一次性读入机器可能吃不消,想着awk可以逐行处理,可能好点
作者: aximofu    时间: 2010-05-18 14:55
回复  where27


   这个也有局限性,domain里面都是2段的域名,如果有3段如sohu.com.cn就出错了
如果 ...
99超人 发表于 2010-05-18 14:41



    是的,我之前在提取域名的时候就遇到这个问题,有可能碰到www.abc.com.cn,def.ab.cn,甚至就一个域名gh.org,统一都按两端域名的话会出问题,我是倒过来取的,而且还要分有无cn这种国家后缀的域名考虑
作者: aximofu    时间: 2010-05-18 15:02
字符少了不让发
bbgg1983 发表于 2010-05-18 14:23



    谢谢,您的方法不错。想问一下,read的话是每读取一行处理一行吗?还是一次性的全部读取到内存中再做处理
作者: aximofu    时间: 2010-05-18 15:11
if($0~i) 这段正则如何改的匹配更精准
比如 sites.txt 有一条  xx.abcbaidu.com
这样就会错,要改成$0~.i ...
99超人 发表于 2010-05-18 14:16


$0 ~/\.abc.com$/


您是想要这个表示吗
作者: springwind426    时间: 2010-05-18 15:19

  1. 如果不确定域名是多长的话,用循环判断,大概还是可以的

  2. awk 'NR==FNR{gsub(/[.]/,"\\.");a[$0"$"]=0}
  3. NR>FNR{for(i in a)if($0~i){a[i]++;break}}
  4. END{for(i in a){v=a[i];gsub(/\\\./,".",i);sub(/\$/,"",i);print i "\t" v }}' domain.txt  sites.txt

  5. 思路就是在读取domain.txt的时候,记录每个域名,并把域名中的.转换成 \. 并在后面加一个$
  6. 在读取sites.txt的时候,依次把domain.txt中的域名与当前行比较,从尾部比较,有的话,就把相应的域名的计数加1

  7. 最后,再遍历域名,做一番转换,并显示结果,如果在sites.txt中没有出现,就显示为0
复制代码

作者: where27    时间: 2010-05-18 15:29
本帖最后由 where27 于 2010-05-18 15:32 编辑

回复 21# springwind426


    你这个好像不能解决5楼的问题。。出现类似www.abcbaidu.com也会算进去,怎么优化呢
作者: iori809    时间: 2010-05-18 15:31
回复 1# aximofu


    能否给出你的原始数据啊?比如弄个压缩包放到上面~
这个还挺有意思的~情况很多是吗
作者: 99超人    时间: 2010-05-18 15:31
提示: 作者被禁止或删除 内容自动屏蔽
作者: 99超人    时间: 2010-05-18 15:40
提示: 作者被禁止或删除 内容自动屏蔽
作者: iori809    时间: 2010-05-18 15:41
回复 24# 99超人


    果然是超人啊~啥都会
作者: aximofu    时间: 2010-05-18 15:41
回复  aximofu


    能否给出你的原始数据啊?比如弄个压缩包放到上面~
这个还挺有意思的~情况很多是 ...
iori809 发表于 2010-05-18 15:31



    不好意思,原始数据太大了,有好几百m,没法传上来,只能说我再把各种可能出现的情况都列一下
domain.txt:
sohu.com
sina.com.cn
sina.com
baidu.com
tsinghua.edu.cn
google.fr
263.net
基本上domain就这几种形式,既有直接以国家后缀结尾的,也有以.com,.net等结尾的,也有以.com.cn这样结尾的

sites.txt:
www.sina.com.cn
dl.sina.com.cn
blog.sohu.com
mail.263.net
google.cn
www.pl.gansu.gov.cn
.....
而且因为domain就有1千多万条记录,像之前那位用for $DOMAIN in `cat domain.txt`这种将数据整体读入内存的方式是不可取的
作者: BangBull    时间: 2010-05-18 15:42

  1. [root@node1 ~]# awk -F. 'NR==FNR{a[$2]++}NR>FNR{if($1 in a) print $1,a[$1] }' site.txt domain.txt
  2. sina 2
  3. baidu 2
  4. sohu 2
  5. google 1
复制代码

作者: aximofu    时间: 2010-05-18 15:43
没办法,用perl写了个,perl可以支持这种精确的匹配写法
99超人 发表于 2010-05-18 15:31



    谢谢您的帮助,可惜我不会用perl,无法领略您的思路了
作者: iori809    时间: 2010-05-18 15:43
回复 27# aximofu


    辛苦了呵呵~
作者: springwind426    时间: 2010-05-18 15:48
回复 22# where27

刚才是没有考虑那个问题
  1. awk 'NR==FNR{gsub(/[.]/,"\\.");a["\\<" $0"$"]=0}
  2. NR>FNR{for(i in a)if($0~i){a[i]++;break}}
  3. END{for(i in a){v=a[i];gsub(/\\\./,".",i);sub(/\$/,"",i);sub(/^\\</,"",i);print i "\t" v }}' domains.txt sites.txt

复制代码
加个 \<  限定一下,应该可以避免了吧
作者: where27    时间: 2010-05-18 15:51
回复 31# springwind426


    恩,行了,厉害,把非典的问题解决了
作者: 99超人    时间: 2010-05-18 16:00
提示: 作者被禁止或删除 内容自动屏蔽
作者: 99超人    时间: 2010-05-18 16:01
提示: 作者被禁止或删除 内容自动屏蔽
作者: aximofu    时间: 2010-05-18 16:03
非常感谢,我要一段一段分析一下您的代码了
作者: where27    时间: 2010-05-18 16:18
回复 35# aximofu
  1. awk 'NR==FNR{a[$0]=0;next}{for(i in a){j="\\."i;if($0~j)a[i]++}}END{for(i in a)print i,a[i]}' domain.txt sites.txt
复制代码
这个好像就行了
作者: expert1    时间: 2010-05-18 16:25
求31楼的详细解释。
作者: howge    时间: 2010-05-18 16:44
回复 16# expert1

为什么要用FS呢,之前你写的那个就好了呀!
作者: iori809    时间: 2010-05-18 16:59
回复 36# where27
看看春哥的吧~你的这个还不行~春哥的功力没的说啊

domain.txt:
sohu.com
sina.com.cn
sina.com
baidu.com
tsinghua.edu.cn
google.fr
263.net
基本上domain就这几种形式,既有直接以国家后缀结尾的,也有以.com,.net等结尾的,也有以.com.cn这样结尾的

sites.txt:
www.sina.com.cn
dl.sina.com.cn
blog.sohu.com
mail.263.net
google.cn
www.pl.gansu.gov.cn
.....
作者: where27    时间: 2010-05-18 17:01
回复 39# iori809


    哪种情况的不行呢?
作者: iori809    时间: 2010-05-18 17:03
回复 40# where27


    sina.com        0
sina.com.cn     2
sohu.com        1
google.fr       0
263.net 1
baidu.com       0
tsinghua.edu.cn 0

上面是春哥的执行后结果
sina.com 2
sina.com.cn 2
tsinghua.edu.cn 0
sohu.com 1
263.net 1
google.fr 0
baidu.com 0
这是你的
作者: where27    时间: 2010-05-18 17:13
回复 41# iori809
  1. awk 'NR==FNR{gsub(/ /,"",$0);a[$0]=0;next}{for(i in a){j="\\."i;if($0~j)a[i]++}}END{for(i in a)print i,a[i]}' domain.txt sites.txt
复制代码
这个行吗
作者: iori809    时间: 2010-05-18 17:14
回复 42# where27


    不行呵呵~
作者: howge    时间: 2010-05-18 17:15
回复 37# expert1

关键就在if部分的$0 ~i 的这个部分 ,i 此时应该是  \<sina\.com$(举例)  awk 正则
作者: where27    时间: 2010-05-18 17:17
回复 43# iori809


   
春哥的确是牛,可能我考虑到的情况不够
作者: aximofu    时间: 2010-05-18 17:28
不好意思,几位,我看springwind426 和where27的awk都是先把domain的内容读到数组里头再利用i在sites里头做检索,这样还是要把domain整体读到内存里头,我的domain里头有1kw条数据,这样还是处理不过来啊,在不split的情况下
作者: aximofu    时间: 2010-05-18 17:35
本帖最后由 aximofu 于 2010-05-18 17:36 编辑

不知道我是不是对两位的理解有问题,但是我刚才找springwind426的脚本执行,之后报错,can't allocate memory (Cannot allocate memory),就是系统资源上吃不消了
以前碰到这个问题,我是用split分割来解决的,不知道有没有更好的解决办法
作者: iori809    时间: 2010-05-18 17:41
回复 47# aximofu


   你的数据量太大啦~而处理需要调用很多函数~看来大数据量处理还是Perl更强大点~
在想想看吧~ 你可以先到PERL版发个帖子。先解决问题。
作者: expert1    时间: 2010-05-18 17:47
用perl吧。或Python也可以^_^。
作者: aximofu    时间: 2010-05-18 17:48
好的,谢谢几位的帮助,我先试试99超人的perl脚本
作者: iori809    时间: 2010-05-18 17:49
回复 50# aximofu


    最后把结果跟大家说说啊~看看Perl到底有多厉害~
作者: aximofu    时间: 2010-05-18 18:00
哈哈。用perl也是报错Out of memory!

看来只能split了
作者: springwind426    时间: 2010-05-18 21:42
本帖最后由 springwind426 于 2010-05-18 21:49 编辑

  1. 1kw的domain列表?

  2. 那 sites.txt 有多少行?

  3. 还有,最后的结果,会有多少行的唯一domain?
复制代码

作者: aximofu    时间: 2010-05-19 09:38
domain都是没有重复的,sites.txt也有1200w条
作者: blackold    时间: 2010-05-19 09:55
回复 54# aximofu


   一定要awk吗?
作者: iori809    时间: 2010-05-19 09:57
回复 55# blackold


    黑哥这个用Perl能搞定吗? 1千万行?
作者: blackold    时间: 2010-05-19 10:06
回复 56# iori809


    这还搞不定,还用shell干吗?
作者: iori809    时间: 2010-05-19 10:08
回复 57# blackold


    哈哈~看来PERL就是用来干这个的~昨天“超人99”写了一个PERL的。
那哥们试了以后也说内存溢出~
黑哥来个犀利点的HOHO~~~~~~
作者: blackold    时间: 2010-05-19 10:10
这个不行吗?
$ wc -l sites.txt
17281532 sites.txt

$ while read line;do echo $line: $(grep -Ec "${line//./\.}\$" sites.txt);done<domain.txt
sina.com: 4937581
baidu.com: 4937580
sohu.com: 4937580
google.com: 0

作者: iori809    时间: 2010-05-19 10:14
黑哥你那也有这样的数据吗?
无语了。。。。。。。
作者: yuloveban    时间: 2010-05-19 12:10
回复 1# aximofu


    awk '/sina.com/' | wc -l
作者: aximofu    时间: 2010-05-19 13:18
这个不行吗?
blackold 发表于 2010-05-19 10:10


谢谢黑哥,您的方法跟7L bbgg1983的方法差不多。
只是有一个小小的疑问
read的处理方式,是每从domain.txt中读取一个输入,处理一个吗
作者: aximofu    时间: 2010-05-19 13:20
回复  aximofu


    awk '/sina.com/' | wc -l
yuloveban 发表于 2010-05-19 12:10



    您这个操作之前就说过了,是针对单个域名的处理,大批量处理的话这样做不成
作者: springwind426    时间: 2010-05-19 14:09
这个不行吗?
blackold 发表于 2010-05-19 10:10



    看这个代码,似乎 domain.txt 文件只有几行

而LZ的domain.txt 和 site.txt 都是有1千多万行的
作者: blackold    时间: 2010-05-19 14:38
回复 64# springwind426


    两个都是千万行?那速度肯定没有那么快了。
作者: blackold    时间: 2010-05-19 14:44
回复 62# aximofu


    对啊,逐行处理。
作者: aximofu    时间: 2010-05-19 15:53
哦。谢谢解惑。
确实是很慢,我用7L的脚本在后台跑,大约十来分钟处理了5000条domain记录
作者: blackold    时间: 2010-05-19 16:18
回复 67# aximofu


    这与sites.txt的行数有关。
作者: iori809    时间: 2010-05-19 16:23
这个帖子火了~
作者: lucash    时间: 2010-05-19 17:04
回复 16# expert1

  1. awk 'NR==FNR{a[$0];next}{for(i in a){x=length(i);y=length($0);if(substr($0,y-x) ~ i) c[i]++}}END{for(j in c) print j,c[j]}' a b
  2.    
复制代码

  1. =====# cat a
  2. sohu.com
  3. sina.com.cn
  4. sina.com
  5. baidu.com
  6. tsinghua.edu.cn
  7. google.fr
  8. 263.net

  9. =====# cat b
  10. www.sina.com.cn
  11. dl.sina.com.cn
  12. blog.sohu.com
  13. mail.263.net
  14. www.51sohu.com
  15. lb.sohu.com.cn
  16. www.19263.net
  17. google.cn
  18. www.pl.gansu.gov.cn

复制代码

作者: wolfkin    时间: 2010-05-19 17:25
for p in `cat domain.txt`
do
echo $p
grep -c -w $p site.txt
done
作者: lucash    时间: 2010-05-19 19:53
用shell处理这么大的不好,效率太低
作者: lucash    时间: 2010-05-19 20:02
本帖最后由 lucash 于 2010-05-19 20:04 编辑

初学perl
用perl写了个,不过效率不怎么样。

============ # wc -l b
243208 b
============ # wc -l a
61994 a
============ # cat a.pl
  1. #!/usr/bin/perl -w

  2. open A,"<a";
  3. open B,"<b";

  4. while(<A>){
  5.     chomp;
  6.     $hash{$_} = undef;
  7. }
  8. close A;

  9. while(<B>){
  10.     chomp;
  11.     my $b = $_;
  12.     foreach(keys %hash){
  13.         if(substr($b,length($b)-length($_)) =~ /$_/){
  14.             $count{$_}++;
  15.         }
  16.     }   
  17. }
  18. close B;

  19. foreach(keys %count){
  20.     print "$_  ==>  $count{$_}\n";
  21. }
复制代码
============ # time perl a.pl a b
263.net  ==>  58216
sohu.com  ==>  54720
sina.com.cn  ==>  43472

real    0m9.355s
user    0m9.261s
sys     0m0.056s
作者: expert1    时间: 2010-05-19 21:34
本帖最后由 expert1 于 2010-05-19 21:36 编辑

awk 'NR==FNR{a[$0];next}{for(i in a){x=length(i);y=length($0);if(substr($0,y-x) ~ i) c++}}END{for(j in c) print j,c[j]}' a b
   

    这里a[$0];next是什么意思啊?难道x是a的内容长度,y是b的长度?
作者: zhiwood    时间: 2010-05-19 22:19
  1. awk -F. '{print $2"."$3}' sites.txt | sort | uniq -c > occurances
  2. sort domain.txt > sorteddoman
  3. join -1 1  -2 2 sortedomain occurances
复制代码

作者: dolphin1987    时间: 2010-05-20 00:22
本帖最后由 dolphin1987 于 2010-05-20 00:24 编辑

回复 54# aximofu


while read line;do echo "$line:"$(grep -c $line sites.txt);done <domain.txt

首先声明自己是刚开始学shell的,好多东西不懂,不过看到你们讨论的这么激烈,我也发表一下自己的观点! 下面是我看法
这个脚本运行速度慢的原因是,每读取domain.txt文件中的一条记录
为了得出这个记录在sites.txt文件中的次数都会遍历一次sites.txt文件
由于sites.txt文件非常大,所以非常费时间。
思路:根据lz的要求,domain.txt 文件非常大,无法全部装入内存,所以
把domain.txt文件拆分,根据机器的配置分批读取站点,每次读取一定量的站点数来统计
出现次数,这样统计一次就只需要遍历一次sites.txt文件。每次读取的站点数越多,那么遍历sites.txt文件的次数就越少,
这样子应该就可以节省不少的时间。比如:每次读取10,000个记录,那么就是以用上面统计效率的
10,000倍。统计的时候用awk。

自己没有那么大的数据,所以没有办法测试。
  1. [root@XXX]# cat calc_site.awk
  2. #!/bin/awk -f
  3. NR==FNR { array[$0]=0}
  4. NR>FNR {
  5.          for (domain_origial in array)
  6.          {      domain="\\."domain_origial
  7.                  if($0~domain)
  8.                          array[domain_origial]++
  9.                  break
  10.          }
  11. }
  12. END{for (domain in array) print domain,array[domain]}
  13. [root@XXX]# cat calc_site.sh
  14. #!/bin/sh
  15. # 这里改一次读取多少个记录
  16. once_num_record=1
  17. #split the big file into small files
  18. split -l $once_num_record $1 once_fetch_file
  19. for site in $(ls once_fetch_file*)
  20. do
  21. #这里是可执行awk脚本放的位置
  22.         /home/XXX/calc_site.awk $site $2
  23. done
  24. rm -f once_fetch_file*
  25. [root@XXX]# ./calc_site.sh  domain.txt  sites.txt
  26. sina.com 2
  27. baidu.com 3
  28. sohu.com 2
  29. google.com 0
复制代码

作者: Shell_HAT    时间: 2010-05-20 00:35
回复 74# expert1


next的意思就是go to next line
也就是说,当读第一个文件a的时候(NR==FNR),只执行a[$0],后面那个花括号里面的内容不执行。
作者: regonly1    时间: 2010-05-20 09:33
awk的特长是列处理吧?你这里用的有点不合场景了么,试试这个:
cat stat.sh
#!/bin/sh
file="domain";
fctt=`cat $file`;
ff="site";

for i in $fctt; do
    cnt=`grep "$i" $ff | wc -l`;
    echo $i, $cnt;
done;

[wanghf@wanghf|ll]$./stat.sh
sina.com, 2
baidu.com, 2
sohu.com, 2
google.com, 0
作者: olivenan    时间: 2010-05-20 11:09
本帖最后由 olivenan 于 2010-05-20 11:10 编辑

awk -F "." '{if(NR==FNR){a[$0]} else {b[$2"."$3]++}}END {for (i in a) print i"   "b}' domain.txt sites.txt
这个的问题是,当sites没有这个域名是返回的是空,不是0
#awk -F "." '{if(NR==FNR){a[$0]} else {b[$2"."$3]++}}END {for (i in a) print i"   "b}' domain.txt sites.txt   
sina.com   2
sohu.com   2
baidu.com   2
google.com
作者: 76862157    时间: 2010-05-20 14:14
for i in `cat aa` ; do echo -n "$i "; grep ${i}$ bb|wc -l ; done

来个非awk的
作者: 做人不能太可鄙    时间: 2010-05-21 00:54
提示: 作者被禁止或删除 内容自动屏蔽
作者: iori809    时间: 2010-05-23 15:25
awk 'NR==FNR{array[$0]}NR>FNR{for(i in array){if($0 ~ i&&substr($0,length($0),1)==substr(i,length(i),1)){array[i]++}}}END{for(i in array){printf i" ";print array[i]==null?"0":array[i]}}' linux.154 linux.155
sina.com 0
sina.com.cn 2
tsinghua.edu.cn 0
sohu.com 1
263.net 1
google.fr 0
baidu.com 0
作者: iseeicando    时间: 2010-05-27 16:10
awk -F'[.]' 'NR==FNR{++S[$2];next}{print $0,S[$1]}' sites.txt domain.txt




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2