タスク
ここでのパラメータはファイル名です。ファイルにはテキストが含まれています。スクリプトのタスクは、他の単語にどの単語が最も頻繁に含まれるかを判断することです。
入力と出力の例
(例: プレー ボール フットボール バスケットボール スノーボール - したがって、ボールは他の 3 つの世界の一部であるため、勝者となります)。
これまでの私のコード
これまでこのコードを実行しましたが、すべての出力で機能するわけではありません
!/bin/sh
awk '{for(i=2;i<NF;i++) {s=$i; for(j=i+1;j<=NF;j++) print s=s FS $j}}' $1 | sort | uniq -c | sort -k1,1rn -k2 | sed 's/ *[^ ]* *//;q' | cut -f1 -d" "
答え1
単語リストが というファイルにありwords
、各行に1つの単語が含まれている場合(tr ' ' '\n' <originalwords >words
元のリストを複数行に分割するために で作成された可能性があります)、ループは
while IFS= read -r word; do
grep -F -o -e "$word" words
done <words | awk '{ c[$0]++; if (c[$0] > c[w]) w = $0 } END { print w }'
リスト内の単語の中で最も多く出現する単語を出力します (または、多くの単語が同じ回数出現する場合は、リスト内で最初に出現した単語の 1 つを出力します)。
これは、リスト自体をリストと照合するパターンのセットとして使用することで行われます。 では、-o
一致する部分文字列が個々の行で返されるように要求します。
質問で与えられたリストのループのみの出力は次のようになります。
play
ball
ball
ball
ball
football
basketball
snowball
後は、これらの単語を数えて、最も頻繁に出現する単語を選び出すだけです。
一時ファイル処理を含む完全なスクリプトとして:
#!/bin/sh
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT # delete temporary file upon exiting
tr -s ' ' '\n' <"${1:-/dev/stdin}" >"$tmpfile" # convert into word list
while IFS= read -r word; do
grep -F -o -e "$word" "$tmpfile"
done <"$tmpfile" | awk '{ c[$0]++; if (c[$0] > c[w]) w = $0 } END { print w }'
ファイルが指定されていない場合、スクリプトは標準入力からさらに読み取ります。
答え2
awk '{
for (i=1; i<=NF; i++) {
uwords[$i] = 0
allwords[++idx] = $i
}
}
END {
if (idx == 0) exit
max = 0
for (w in uwords) {
count = 0
for (i=1; i<=idx; i++) {
if (allwords[i] ~ w) count++;
}
if (count > max) {
max = count
maxw = w
}
}
print maxw
}'
入力をスキャンし、一意の単語のリストとすべての単語のリストを抽出します。(一意の単語のリストは必要ないと思いますが、入力が大きい場合は、より効率的になる可能性があります。) 次に、一意の単語ごとに、ファイル内で一致する単語の数を数えます。(つまり、ファイルに が含まれている場合football football football
、 に対して 3 がカウントされますball
。) 最も一致が多い単語を追跡します。
同点の場合は、uwords
(一意の単語)配列で最初に現れる単語を報告します。これは、必ずしもファイル内で最初に現れる単語ではなく、アルファベット順で最初でもありません。
単語に以下のものが含まれている場合、予期しない結果が生じる可能性があります。.
、*
または[
。
Kusalananda の shell+awk アプローチを好むが、エッジケース エラーは避けたい場合は、次のようにします。
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT # delete temporary file upon exiting
tr -s ' ' '\n' < "${1:-/dev/stdin}" > "$tmpfile" # convert into word list
sort -u "$tmpfile" | while IFS= read -r word
do
grep -F -o -e "$word" "$tmpfile"
done | awk '{ c[$0]++; if (c[$0] > c[w]) w = $0 } END { print w }'
単語リストを並べ替えると、一意の単語のリストが得られるため、単語が複数回カウントされることがなくなります。
このコードでは、入力ファイルが最大で 1 つであると明示的に想定していることに注意してください (ただし、ファイルが存在しない場合もあります。つまり、stdin から読み取られる場合もあります)。これは質問の表現と一致しています。ただし、入力ファイルが任意の数 (0、1、以上)tr
の行を次のように変更します。
cat -- "$@" | tr -s ' ' '\n' > "$tmpfile" # convert into word list
おそらくUUOCですが、
- 2つ以上の入力ファイルがある場合も処理し、
- よりも読みやすいです
< "${1:-/dev/stdin}"
。