我有一个字符串列表(stdin),如下:
1 pineapples
28 apples
16 oranges
8 apples
2 apples
2 oranges
56 pineapples
是否有一种原生的方式(如sort
和uniq -c
),我可以合并并将它们总结如下:
38 apples
18 oranges
57 pineapples
像sort |uniq -c
那样,但不仅仅是因为出现数量?
试试这个:
awk '{a[$2] += $1} END{for (i in a) print a[i], i}' < in.txt
输出
38 apples
57 pineapples
18 oranges
使用GNU datamash
:
$ <file datamash -Wst' ' -g2 sum 1
apples 38
oranges 18
pineapples 57
(-W
使用空格作为输入字段分隔符,-t' '
使用空格作为输出字段分隔符,-s
排序输入,-g2
组按列2,sum 1
汇总值来自每组中的第1列。)
这里不是一个大赢家(超过awk
),但它确实对更复杂的统计操作(例如计算组中位数,方差,偏度等)有所启发。
为了得到有问题的格式(如果这很重要),我们需要手动反转输出字段的顺序,因为datamash
总是先输出分组列:
$ <file datamash -Wst' ' -g2 sum 1 | datamash -Wt' ' reverse
38 apples
18 oranges
57 pineapples
Awk是这项工作的正确工具。然而,对于那些不熟悉awk且bash版本> = 4.0的人,这里有一个替代版本的bash associative arrays。这将读取文件Fruits的每一行,并使用第二列作为键存储数字。
declare -A Sumarray
while IFS=" " read num thing
do
if [[ -v Sumarray[$thing] ]]
then
Sumarray[$thing]=$(( ${Sumarray[$thing]} + $num ))
else
Sumarray[$thing]=$num
fi
done < Fruits
$ for K in "${!Sumarray[@]}"; do echo ${Sumarray[$K]} $K ; done
38 apples
57 pineapples
18 oranges
使用awk
对值进行求和并对输出进行排序:
awk '{
items[$2]+=$1
}
END {
asorti(items, sorted)
for(i in sorted)
print items[sorted[i]] " " sorted[i]
}' input_file
仅使用bash
:
declare -A items=()
while read -r num item; do
((items[$item] += num))
done < input_file
sorted=()
while IFS= read -r -d '' item; do
sorted+=("$item")
done < <(printf '%s\0' "${!items[@]}" | sort -z)
for index in "${sorted[@]}"; do
echo "${items[$index]} $index"
done