Bash의 입력을 기반으로 점수 매기기

Bash의 입력을 기반으로 점수 매기기

도와주세요? 나에겐 임무가 있다. 숫자가 포함된 텍스트를 입력했습니다. 예를 들어:

beta     1
score   9
something   2
beta     4
something   1

주위에 같은 텍스트가 있는 모든 숫자를 세어야 합니다. 내 출력은 다음과 같습니다. (이 방법으로 ":"을 사용하십시오)

beta:5
something:3
score:9

또한, 점수를 저장하는 임시 파일에 문제가 있을 수 있습니다. mktemp스크립트가 완료된 후 삭제하는 데 사용해야 합니다 . 도와주세요, 감사합니다.

답변1

입력에는 항상 한 줄에 두 개의 필드가 포함되어 있다고 가정합니다.

GNU 유틸리티를 사용하여 datamash데이터를 정렬하고, 첫 번째 필드별로 그룹화하고, 각 그룹에 대한 두 번째 필드의 합계를 계산할 수 있습니다.

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블록은 입력이 끝나면 실행되며 계산된 합계를 문자열과 함께 출력합니다.

또한 이 솔루션은 질문에 표시된 것처럼 첫 번째 필드가 공백 문자를 포함하지 않는 단일 단어라고 가정합니다.


쉘 루프를 사용하여 원본 파일에서 정렬된 행을 읽고 새로운 첫 번째 필드가 나타날 때마다 두 번째 필드의 합계를 인쇄하고 재설정합니다.

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

관련 정보