假设您有一个包含IP地址的文件,每行包含一个地址:
10.0.10.1 10.0.10.1 10.0.10.3 10.0.10.2 10.0.10.1
您需要一个shell脚本,它为每个IP地址计算它在文件中出现的次数.对于先前的输入,您需要以下输出:
10.0.10.1 3 10.0.10.2 1 10.0.10.3 1
一种方法是:
cat ip_addresses |uniq |while read ip do echo -n $ip" " grep -c $ip ip_addresses done
然而,它真的远没有效率.
如何使用bash更有效地解决这个问题?
(有一点要补充:我知道它可以通过perl或awk解决,我对bash中的更好解决方案感兴趣,而不是那些语言.)
附加信息:
假设源文件为5GB,运行算法的机器为4GB.因此,排序不是一种有效的解决方案,也不是多次读取文件.
我喜欢类似哈希表的解决方案 - 任何人都可以对该解决方案进行改进吗?
附加信息#2:
有些人问为什么我会在bash中使用它时更加困难,例如perl.原因是在机器上我必须这样做perl不适合我.这是一个定制的linux机器,没有我习惯的大多数工具.我认为这是一个有趣的问题.
所以,请不要责怪这个问题,如果你不喜欢它就忽略它.:-)
sort ip_addresses | uniq -c
这将首先打印计数,但除此之外它应该是您想要的.
快速而肮脏的方法如下:
cat ip_addresses | sort -n | uniq -c
如果需要使用bash中的值,可以将整个命令分配给bash变量,然后遍历结果.
PS
如果省略sort命令,则无法获得正确的结果,因为uniq仅查看连续的相同行.
为了总结多个字段,基于一组现有字段,使用以下示例:(根据您的要求替换$ 1,$ 2,$ 3,$ 4)
cat file US|A|1000|2000 US|B|1000|2000 US|C|1000|2000 UK|1|1000|2000 UK|1|1000|2000 UK|1|1000|2000 awk 'BEGIN { FS=OFS=SUBSEP="|"}{arr[$1,$2]+=$3+$4 }END {for (i in arr) print i,arr[i]}' file US|A|3000 US|B|3000 US|C|3000 UK|1|9000
规范解决方案是另一位受访者提到的解决方案:
sort | uniq -c
它比Perl或awk中编写的更简洁,更简洁.
您写道,您不想使用排序,因为数据的大小大于计算机的主内存大小.不要低估Unix sort命令的实现质量.Sort用于处理具有128k(即131,072字节)内存(PDP-11)的计算机上的大量数据(比如原始AT&T的计费数据).当排序遇到的数据多于预设限制(通常调整到接近机器主存储器的大小)时,它会对它在主存储器中读取的数据进行排序并将其写入临时文件.然后它使用下一个数据块重复该操作.最后,它对这些中间文件执行合并排序.这允许排序处理比机器主存储器大许多倍的数据.
cat ip_addresses | sort | uniq -c | sort -nr | awk '{print $2 " " $1}'
这个命令会给你想要的输出