我有一个包含两个字段的大文件,第一个字段表示对象名称,第二个字段表示该对象的大小:
A 1
A 2
B 4
ABC 12
C 5
A 9
B 3
ABC 6
我想用以下格式总结该列表:
A 1,2,9
ABC 12,6
B 4,3
C 5
我想出的解决方案是创建文件中存在的对象的唯一列表,迭代它并将其与原始文件匹配
for object in $(awk '{print $1}' objects_with_sizes.txt | sort -u);do
echo -n "$object "
awk -v pattern="$object" '$1==pattern{printf "%s%s" ,sep,$2;sep=","} END{print ""}' objects_with_sizes.txt
done
此实现需要很长时间才能运行,是否有更有效的方法来创建所需的输出?
答案1
$ awk '{ object[$1]= (object[$1]==""?"":object[$1] ",") $2 }
END { for(obj in object) print obj, object[obj] }' infile
A 1,2,9
B 4,3
C 5
ABC 12,6
更有效一点(使用内存;对于无法放入内存的大文件很重要),即不将文件部分缓冲到上面单独使用 awk 命令执行的操作,但仅直到对象键发生更改为止:
$ <infile sort -k1,1 -k2,2n |\
awk 'pre!=$1 { if(obj) { print obj; obj="" } }
{ obj= (obj==""?$1 " ":obj ",") $2; pre=$1 }
END{ if(obj) print obj }'
A 1,2,9
ABC 6,12
B 3,4
C 5
答案2
使用 GNU datamash
:
$ datamash -t ' ' -s -g 1 collapse 2 <file
A 1,2,9
ABC 12,6
B 4,3
C 5
选项:
-t '_'
使用空格字符作为字段分隔符-s
分组前对输入进行排序-g 1
第一个字段上的组collapse 2
将第二个字段的值折叠到逗号分隔的列表中
答案3
我们可以排序然后输入到 GNU sed,它将当前的第一个字段与前一个字段进行比较,以生成逗号分隔的 OR 打印直到该点。
$ < file sort -s -k1,1 \
| sed -Ee '
:a
$!N
s/^((\S+)\s.*)\n\2\s+(\S+)/\1,\3/
ta
P;D
' -
A 1,2,9
ABC 12,6
B 4,3
C 5