awk를 사용하고 디렉토리의 파일을 반복합니다.

awk를 사용하고 디렉토리의 파일을 반복합니다.

hotel_72572.dat와 같은 다른 파일이 포함된 Reviews_folder가 있습니다.

각 파일에는 다음과 같이 구성된 많은 주석이 포함되어 있습니다.

...
<Overall>4
...

내 목표는averagereviews.sh 스크립트를 사용하여 각 파일(호텔)에 대한 모든 리뷰의 평균 총 개수를 계산하는 것입니다. 다음을 실행하면 ./averagereviews.sh path_to_reviews_folder 다음과 같은 결과가 출력됩니다.

hotel_11212.dat 3.51
hotel_2121.dat 2.62
hotel_31212.dat 2.43
...

내 스크립트는 다음과 같습니다

#!/bin/bash
cd "$1" || exit 1
for file in "$1"; do
awk 'count+=sub(/<Overall>/, ""){sum+=$0}END{print sum/count}' file;
done

문제는 파일을 디렉터리로 인식하지 못하고 hotel_*.dat를 넣으면 각 파일이 아닌 review_folder에 있는 모든 기존 파일의 평균을 계산한다는 것입니다.

답변1

싱글로awk스크립트( for루프 및 다중 awk호출 없음):

입력 파일 예:

$ head reviews_folder/hotel_*.dat
==> reviews_folder/hotel_111.dat <==
<Overall>1
<Overall>4
<Overall>3

==> reviews_folder/hotel_222.dat <==
<Overall>11
<Overall>5
<Overall>7

==> reviews_folder/hotel_333.dat <==
<Overall>7
<Overall>4
<Overall>10

awk -F'>' 'fn && FILENAME != fn{ 
              sub(".*/", "", fn);
              print fn, sprintf("%.2f", sum/n); sum = 0
          }
          { sum += $2; n = FNR; fn = FILENAME }
          END{ 
              sub(".*/", "", fn);
              print fn, sprintf("%.2f", sum/n)
          }' reviews_folder/hotel_*.dat

산출:

hotel_111.dat 2.67
hotel_222.dat 7.67
hotel_333.dat 7.00

답변2

스크립트를 일부 개선하여

#!/bin/bash
cd "$1" || { printf 'unable to navigate to target\n' >&2; exit 1 ; }
for file in *.dat; do
    test -f "$file" || continue
    awk 'count+=sub(/<Overall>/, ""){sum+=$0}END{print (count)?(sum/count):0}}' "$file"
done
  1. 필요하지 않은 파일 확장자를 cd-ing했기 때문에 루프를 반복하십시오."$1"for file in "$1"for file in *.dat
  2. 이 조건은 보고 있는 경로에 파일이 없는 경우 처리를 위해 test -f "$file" || continue확장되지 않은 glob을 전달하는 대신 for 루프가 정상적으로 종료되도록 보장합니다.awk
  3. $file리터럴 string 대신 파일 이름을 as로 전달합니다 file. 셸 변수는 $이름 앞에 기호를 붙여야 하며 일반적으로 큰따옴표로 묶어야 합니다.
  4. ENDawk나누기 전에 개수가 0이 아닌지 확인하기 위한 절의 작은 개선 사항입니다 .

답변3

for file in "$1"루프를 한 번 실행하고 file스크립트 첫 번째 인수의 리터럴 값으로 설정됩니다. 그 안의 와일드카드 문자는 "$1"인용 으로 인해 확장되지 않습니다. 디렉터리를 스크립트에 전달하면 디렉터리 이름도 전달하게 되는데 awk, 이는 그다지 마음에 들지 않을 수도 있습니다 gawk.

gawk: warning: command line argument `/tmp/test/' is a directory: skipped

각 파일에 대해 개별적으로 루프를 실행하려면 적절한 곳에 와일드카드를 사용하십시오. 이는 *현재 디렉토리의 파일 이름으로 확장되며, cd방금 거기에 하나를 만들었기 때문에 인수로 제공됩니다.

#!/bin/sh
cd "$1" || exit 1 
for file in * ; do
    awk '...' "$file"
done

또는 파일 이름 목록을 스크립트에 대한 인수로 전달한 후 다음을 반복할 수 있습니다.

#!/bin/sh
for file in "$@" ; do
    awk '...' "$file"
done

실제로, 이를 수행 myscript /some/path/hotel*.dat하고 쉘이 파일 이름을 스크립트 명령줄로 확장하도록 할 수 있습니다. "$@"명령줄 인수 목록으로 확장됩니다.


즉, awk대본에도 약간의 문제가 있습니다. 작성하신 대로 첫 번째 규칙의 조건은 입니다 count+=sub(/<Overall>/, ""). 이는 count추가가 0이 아닌 한 sub()이번에 반환되는 내용에 관계없이 적용됩니다 . 즉, 규칙 이 한 번 이상 표시될 {sum+=$0}때마다 규칙이 실행됩니다. <Overall>더하지 않고 합산됩니다 count.

다음과 같은 것을 원할 수도 있습니다.

awk '/^<Overall>/ {sub(/<Overall>/, ""); count += 1; sum += $0} END {print sum/count}' "$file"

파일 이름을 표시하려면 다음을 수행하십시오 echo.

#!/bin/sh
cd "$1" || exit 1 
for file in * ; do
    printf "%s " "$file"
    awk '/^<Overall>/ {sub(/<Overall>/, ""); count += 1; sum += $0} END {print sum/count}' "$file"
done

답변4

각 파일에 대해 다음 명령을 사용하면 평균을 얻을 수 있습니다. 테스트를 거쳐 잘 작동함

입력하다

<Overall>1
<Overall>4
<Overall>3

i=`awk '{print NR}' hotel_111.dat| tail -1 `

awk -F ">" -v i="$i" 'BEGIN{sum=0} {sum=sum+$2} END{print FILENAME;print  sum/i}' hotel_111.dat  | sed "N;s/\n/ /g"

산출

hotel_111.dat 2.66667

관련 정보