在bash中对键进行排序和求和

问题描述 投票:6回答:4

我有一个字符串列表(stdin),如下:

1 pineapples
28 apples
16 oranges
8 apples
2 apples
2 oranges
56 pineapples

是否有一种原生的方式(如sortuniq -c),我可以合并并将它们总结如下:

38 apples
18 oranges
57 pineapples

sort |uniq -c那样,但不仅仅是因为出现数量?

bash awk
4个回答
13
投票

试试这个:

awk '{a[$2] += $1} END{for (i in a) print a[i], i}' < in.txt

输出

38 apples
57 pineapples
18 oranges

4
投票

使用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

0
投票

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

0
投票

使用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
© www.soinside.com 2019 - 2024. All rights reserved.