根据 Bash 中的输入进行评分

根据 Bash 中的输入进行评分

你能帮我吗?我有一个任务。我输入了一些带有数字的文本。例如:

beta     1
score   9
something   2
beta     4
something   1

我需要计算周围具有相同文本的所有数字。我的输出将是:(以这种方式使用“:”)

beta:5
something:3
score:9

另外,临时文件也可能有问题,我可以在其中保存我的分数。我需要mktemp在脚本完成后使用来删除它。请帮助我,谢谢。

答案1

我假设输入每行始终包含两个字段。

您可以使用 GNUdatamash实用程序对数据进行排序,按第一个字段进行分组,并计算每组第二个字段的总和:

datamash -s -W --output-delimiter=: groupby 1 sum 2 <file

此处,对-s输入进行排序,-W使实用程序将任何连续的空白字符视为字段分隔符,并将--output-delimiter=:输出分隔符设置为该:字符。其余部分告诉datamash按第一个字段进行分组并计算每组第二个字段的总和。

给定名为 的文件中问题的输入file,这将产生以下输出:

beta:5
score:9
something:3

您也可以通过许多其他方法来解决这个问题。最简单的计算解决方案是使用awk

awk '{ sum[$1] += $2 } END { for (key in sum) printf "%s:%d\n", key, sum[key] }' file 

在这里,我们使用关联数组sum来保存第一个字段中每个字符串的总和。该END块在输入末尾执行,并将计算出的总和与字符串一起输出。

请注意,此解决方案还假设第一个字段是不包含空格字符的单个单词,如问题所示。


使用 shell 循环,从原始文件中读取排序后的行,每当遇到新的第一个字段时打印并重置第二个字段的总和:

unset -v prev

sort file |
{
        while read -r key value; do
                if [ "$key" != "${prev-$key}" ]; then
                        # prev is set and different from $key

                        printf '%s:%d\n' "$prev" "$sum"
                        sum=0
                fi

                prev=$key
                sum=$(( sum + value ))
        done

        if [ "${prev+set}" = set ]; then
                printf '%s:%d\n' "$prev" "$sum"
        fi
}

有关的:为什么使用 shell 循环处理文本被认为是不好的做法?

答案2

如果您正在处理大文件,请考虑使用sortawk,这样我们就不会在 RAM 中分配巨大的数组来存储键和值。

λ cat input.txt 
beta     1
score   9
something   2
beta     4
something   1
sort input.txt |
  awk -v OFS=: 'NR==1{ key=$1 }; NR>1&&$1!=key{ print key, sum; sum=0; key=$1 }; {sum+=$2} END{ print key, sum}'
beta:5
score:9
something:3

答案3

#!/bin/bash
declare -i SECOND
while read first second; do
        if [ -z $FIRST ] || [ $first = $FIRST ]; then
                SECOND+=second
        else 
                echo $FIRST:$SECOND
                SECOND=second
        fi
        FIRST=$first
done < <(sort file)
echo $FIRST:$SECOND

通常我会写一个类似的空白,并在生产中将所有变量放在引号中。

答案4

 for k in $(awk '{if(!seen[$1]++)print $1}' file.txt); do awk -v k="$k" 'BEGIN{sum=0}$0 ~ k {sum=sum+$2}END{print k,sum}' file.txt; done

输出

beta 5
score 9
something 3

相关内容