Tarefa
O parâmetro aqui é um nome de arquivo! O arquivo contém um texto. A tarefa do script é decidir qual palavra está contida com mais frequência em outras palavras.
Exemplo de entrada e saída
(por exemplo, o texto é: jogar bola futebol basquete bola de neve - portanto a bola é a vencedora porque faz parte de outros três mundos).
Meu código até agora
Eu fiz esse código até agora, mas não funciona para todas as saídas
!/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" "
Responder1
Se a lista de palavras estiver em um arquivo chamado words
, com uma única palavra em cada linha (possivelmente criada para tr ' ' '\n' <originalwords >words
dividir a lista original em múltiplas linhas), então o loop
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 }'
gerará a palavra que ocorre na maioria das vezes como parte das palavras da lista (ou, se muitas palavras ocorrerem igualmente muitas vezes, aquela que ocorreu primeiro na lista).
Isso é feito usando a própria lista como um conjunto de padrões para combinar com a lista. Com -o
pedimos que as substrings correspondentes sejam retornadas em linhas individuais.
A saída do loop sozinha, com a lista dada na questão será
play
ball
ball
ball
ball
football
basketball
snowball
Basta então contar essas palavras e destacar aquela que ocorre com mais frequência.
Como um script completo, com manipulação de arquivos temporários:
#!/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 }'
O script lê adicionalmente a entrada padrão se não houver nenhum arquivo especificado.
Responder2
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
}'
Digitalize a entrada e extraia uma lista de palavras exclusivas e uma lista de todas as palavras. (Acho que não precisamos da lista de palavras únicas, mas isso pode tornar as coisas mais eficientes no caso de uma entrada grande.) Então, para cada palavra única, conte quantas palavras no arquivo correspondem a ela. (Portanto, se o arquivo contiver football football football
, isso contará 3 para ball
.) Acompanhe aquele com mais correspondências.
Em caso de empate, informa a palavra que aparece primeiro no uwords
array (palavras únicas). Este não é necessariamente o primeiro que aparece no arquivo, nem é o primeiro em ordem alfabética.
Isto pode produzir resultados inesperados se alguma das palavras contiver.
, *
ou [
.
Se você preferir a abordagem shell + awk de Kusalananda, mas não deseja o erro extremo, faça o seguinte:
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 }'
Ao classificar a lista de palavras, obtemos uma lista de palavras únicas e, portanto, não contamos nenhuma palavra várias vezes.
Observe que este código assume explicitamente que existe no máximo um arquivo de entrada (mas não pode haver nenhum arquivo; ou seja, lido do stdin). Isso é consistente com a formulação da pergunta. No entanto, se puder haver qualquer número de arquivos de entrada (zero, um,ou mais), altere a tr
linha para
cat -- "$@" | tr -s ' ' '\n' > "$tmpfile" # convert into word list
Provavelmente é um UUOC, mas
- ele lida com o caso de dois ou mais arquivos de entrada, e
- é mais legível do que
< "${1:-/dev/stdin}"
.