使用哈希表是解决此问题的简单方法,bash 4.x 直接支持它,当然也可以在 awk 和 perl 中找到。如果您没有哈希表,则需要循环两次:一次收集第二列的唯一值,一次求和。
有许多方法可以做到这一点。这里有一个有趣的方法,不使用 awk、sed 或 perl。我在这里使用的唯一外部实用程序是 cut、sort 和 uniq。你甚至可以用更多的努力替换 cut
。事实上,第 5-9 行甚至可以更容易地用 grep 写成 (grep $kind stock.txt
),但我避免了这样做,以展示 bash 的强大功能。
for kind in $(cut -d\; -f 2 stock.txt | sort | uniq) ; do
total=0
while read d ; do
total=$(( total+d ))
done < <(
while read line ; do
[[ $line =~ $kind ]] && echo $line
done < stock.txt | cut -d\; -f3
)
echo "Total amount for $kind: $total"
done
在这里我们失去了原始输出的严格排序。一个练习是找到一种不这样做的方法。
讨论:
第一行描述了一个使用简单管道和cut的子shell。我们从stock.txt文件中读取第三个字段,使用;分隔字段,这里写成\;以便shell不会解释它。结果是来自stock.txt的值的以换行符分隔的列表。这被传输到sort,然后uniq。这执行了我们的“分组”步骤,因为管道将输出第二列中的按字母顺序排列的项目列表,但无论输入文件中出现多少次,每个项目只列出一次。
第一行还有一个典型的for循环:对于子shell产生的每个项,我们循环一次,在变量kind中存储该项的值。这是分组步骤的另一半,确保每个“Total”输出行只出现一次。
在第二行中,total初始化为零,以便在开始新组时总是重置。
第三行开始了“总计”循环,在当前的kind
中,我们找到其出现次数的总和。在这里,我们声明每次循环将从stdin读取变量d
。
第四行实际上进行了总计:使用shell算术将d
中的值加到total
中的值。
第五行结束了while循环,然后描述了它的输入。我们使用shell输入重定向通过<
指定循环的输入,因此也指定了read
命令的输入来自文件。然后,我们使用进程替换指定该文件实际上将是一个命令的结果。
在第六行,将开始提供 while-read 循环的命令。它本身是另一个 while-read 循环,这次读入变量 line
。在第七行,通过 条件结构 执行测试。这里我们使用 [[
的 =~
运算符进行测试,它是一种模式匹配运算符。我们正在测试是否 $line
与当前的 $kind
匹配。
在第八行,我们结束内部 while-read 循环,并指定其输入来自 stock.txt
文件,然后将整个循环的输出(现在只是所有匹配 $kind
的行)传输到 cut
并指示它仅显示第三个字段,即数字字段。在第九行,我们结束了进程替换命令,其输出是由 kind
指定的组中的行的换行分隔数字列表。
考虑到现在已知总数和种类,将结果打印到屏幕上就是一件简单的事情。