替代 uniq -c 并在纯 awk 中排序

Alternative to uniq -c and sort in pure awk

这是一个命令,用于从 access.log 文件中按 IP 地址过滤出命中数,然后计算每个 IP 的命中数,并将它们从低到高排序:

awk '{print }' "${ACCESSLOG}" | sort -n | uniq -c | sort -nk1

这里是结果的摘录:

 26 45.59.193.115
 26 74.125.63.33
 27 88.156.36.194
 28 12.208.4.156
 29 12.208.4.156
 31 98.236.117.199
 32 176.9.82.6
 33 187.34.167.111
 35 67.110.83.252
 37 54.184.4.183
 39 195.59.2.173
 39 70.199.109.118
 44 12.208.4.156
 59 88.156.36.194

现在只使用 awk 是否可以得到相同的结果? 没有 uniq -c,没有排序。

似乎在网上找不到太多关于此的信息...

理论上 - 是的,你可以。但是这里有两部分:

你能实现sort和uniq吗?排序将非常棘手,但可以肯定的是,您可以在 awk 中实现任何东西。 Uniq 应该是微不足道的。

你能实施你的管道吗,所以 | uniq -c | sort -nk1 | uniq。是的,这不会很难。只需使用类似:

awk '{ips[]++} END {for (ip in ips) { print ips[ip], ip}}'

这就是计数/唯一性部分。您必须添加 asort 以在末尾对条目进行排序。

GNU awk 具有使计数和排序变得容易的功能:

awk 'BEGIN{PROCINFO["sorted_in"]="@val_num_asc"} { a[]++ } END{for (ip in a)print a[ip],ip}' access.log

语句 PROCINFO["sorted_in"]="@val_num_asc" 使数组根据值而不是键按数字升序排列。

(Mac OSX 上的默认 awk 是 BSD,所以不要在那里尝试。)

例子

假设我们有输入文件:

$ cat access.log
74.125.63.33
45.59.193.115
45.59.193.115
74.125.63.33
74.125.63.33
74.125.63.33
195.59.2.173

然后以上产生:

$ awk 'BEGIN{PROCINFO["sorted_in"]="@val_num_asc"} { a[]++ } END{for (ip in a)print a[ip],ip}' access.log
1 195.59.2.173
2 45.59.193.115
4 74.125.63.33

@viraptor 实际上,我更正了我的命令,因为我们的结果不同,现在看起来更像这样:

awk '{print }' "${ACCESSLOG}" | sort -n | uniq -c | sort -nk1

实际 0m0.020s

用户 0m0.016s

sys 0m0.012s

所以我将 sort 命令添加到你最初的提议中,因为我既不能使用 gawk (asort) 也不能使用 GNU:

awk '{if(ips[]) {ips[]++} else {ips[]=1}} END {for (ip in ips) { print ips[ip], ip}}' "$ACCESSLOG" | sort -nk1

实际 0m0.019s

用户 0m0.004s

sys 0m0.008s

而您的重构命令:

awk '{ips[]++} END {for (ip in ips) { print ips[ip], ip}}' "${ACCESSLOG}" | sort -nk1

实际 0m0.014s

用户 0m0.004s

sys 0m0.012s

比较速度很有趣...