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
リテラル文字列の代わりにファイル名をasに渡しますfile
。シェル変数は$
名前の前に記号を付ける必要があり、通常は二重引用符で囲む必要があります。END
awk
分割する前に数がゼロでないことを確認するための節の小さな改善です。
答え3
for file in "$1"
ループを一度実行し、file
スクリプトの最初の引数のリテラル値に設定されます。その中のワイルドカード文字は"$1"
引用により拡張されません。ディレクトリをスクリプトに渡すと、ディレクトリ名も渡されますがawk
、これはあまり気に入らない可能性がありますgawk
。
gawk: warning: command line argument `/tmp/test/' is a directory: skipped
各ファイルに対して個別にループを実行するには、適切な場所にワイルドカードを使用します。これは*
現在のディレクトリのファイル名に拡張され、ちょうどcd
そこに1つを作成したので、引数として提供されます。
#!/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
、追加がゼロでない限り、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