Bashの入力に基づいてスコア付け

Bashの入力に基づいてスコア付け

助けてください?私には任務がある。数字を含むテキストを入力しました。たとえば、

beta     1
score   9
something   2
beta     4
something   1

周囲に同じテキストを含むすべての数字を数える必要があります。私の出力は次のとおりです。 (このように「:」を使用してください)

beta:5
something:3
score:9

さらに、スコアを保存する一時ファイルに問題がある可能性があります。mktempスクリプトが完了したら削除するために使用する必要があります。助けてくれてありがとう。

答え1

入力には常に1行に2つのフィールドが含まれていると仮定します。

GNUユーティリティを使用してdatamashデータをソートし、最初のフィールドでグループ化し、各グループの2番目のフィールドの合計を計算できます。

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

ここでは、ユーティリティーは連続した空白文字をフィールド区切り文字として扱い、出力区切り文字を対応する文字に設定するように-s入力をソートします。残りは、最初のフィールドに基づいてグループ化し、各グループの2番目のフィールドの合計を計算するように指示します。-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ブロックは入力の終わりに実行され、計算された合計を文字列と共に出力します。

また、この解決策は、質問に示すように、最初のフィールドが空白文字を含まない単一の単語であると仮定します。


シェルループを使用してソースファイルからソートされた行を読み取り、新しい最初のフィールドが表示されるたびに2番目のフィールドの合計を印刷してリセットします。

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
}

関連:シェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされますか?

答え2

大容量ファイルを処理する場合は、キーと値を格納するためにRAMに巨大な配列を割り当てないようにして使用することをsort検討してください。awk

λ 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

関連情報