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
- 필요하지 않은 파일 확장자를
cd
-ing했기 때문에 루프를 반복하십시오."$1"
for file in "$1"
for file in *.dat
- 이 조건은 보고 있는 경로에 파일이 없는 경우 처리를 위해
test -f "$file" || continue
확장되지 않은 glob을 전달하는 대신 for 루프가 정상적으로 종료되도록 보장합니다.awk
$file
리터럴 string 대신 파일 이름을 as로 전달합니다file
. 셸 변수는$
이름 앞에 기호를 붙여야 하며 일반적으로 큰따옴표로 묶어야 합니다.END
awk
나누기 전에 개수가 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