Задача
Параметр здесь — имя файла! Файл содержит текст. Задача скрипта — решить, какое слово чаще всего встречается в других словах.
Пример ввода и вывода
(например, текст: игра в мяч, футбол, баскетбол, снежки - следовательно, мяч является победителем, потому что он является частью трех других миров).
Мой код софа
Я уже сделал этот код, но он не работает для каждого вывода
!/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
, в каждой строке которого находится одно слово (возможно, созданное с помощью , 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 }'
выведет слово, которое встречается чаще всего как часть слов в списке (или, если несколько слов встречаются одинаковое количество раз, то одно из тех слов, которое встречается первым в списке).
Он делает это, используя сам список как набор шаблонов для сопоставления со списком. С помощью -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
массиве (уникальных слов). Это не обязательно первое слово, которое появляется в файле, и не первое в алфавитном порядке.
Это может привести к неожиданным результатам, если какое-либо из слов содержит.
, *
или [
.
Если вы предпочитаете подход Кусалананды 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 }'
Сортируя список слов, мы получаем список уникальных слов и, таким образом, не учитываем ни одно слово несколько раз.
Обратите внимание, что этот код явно предполагает, что существует не более одного входного файла (но файла может и не быть; т. е. чтение из stdin). Это соответствует формулировке вопроса. Однако, если может быть любое количество входных файлов (ноль, один,или больше), измените tr
строку на
cat -- "$@" | tr -s ' ' '\n' > "$tmpfile" # convert into word list
Можно утверждать, что это UUOC, но
- он обрабатывает случай двух или более входных файлов, и
- он более читабелен, чем
< "${1:-/dev/stdin}"
.