假设一个几 GB 且有几百万行的大型日志文件,其中每行都包含一个令牌,用于标识生成该行的用户帐户。
所有标记都具有相同的长度,并且可以在每个日志行内的位置找到。
目标是找出每个帐户记录的字节数。
实现此目的的一种方法是分多个步骤,如下所示:
awk -F "|" '{ print $5 }' trace.log | sort | uniq | xargs -l sh -c 'echo -n $0 && grep "$0" trace.log | wc -c'
其中 awk 提取标记(第 5 个条目通过 '|' 进行标记),sort | uniq 提取文件中出现的唯一标记列表,最后 xargs greps 并计算字节数。
现在它可以工作了,但是效率非常低,因为同一个(巨大的)文件被 grep 了 X 次。
有没有更智能的方法通过 shell 命令实现相同的目的?(更智能的意思是更快并且不消耗大量的 RAM 或临时存储,比如在 RAM 中对整个文件进行排序或将其排序到 tmp 文件)。
答案1
尝试:
awk -F "|" '{ a[$5]+=1+length($0) } END{for (name in a) print name,a[name]}' trace.log
例子
让我们考虑这个测试文件:
$ cat trace.log
1|2|3|4|jerry|6
a|b|c|d|phil|f
1|2|3|4|jerry|6
原始命令产生以下输出:
$ awk -F "|" '{ print $5 }' trace.log | sort | uniq | xargs -l sh -c 'echo -n $0 && grep "$0" trace.log | wc -c'
jerry32
phil15
建议的命令仅循环遍历文件一次,产生以下输出:
$ awk -F "|" '{ a[$5]+=1+length($0) } END{for (name in a) print name,a[name]}' trace.log
jerry 32
phil 15
怎么运行的
-F "|"
这将设置输入的字段分隔符。
a[$5]+=1+length($0)
对于每一行,我们将该行的长度添加到
a
该行用户名下的关联数组中存储的计数中。该数量
length($0)
不包括行尾的换行符。因此,我们在此基础上加一以说明\n
。END{for (name in a) print name,a[name]}
我们读完文件一次后,就打印出总数。