Aufgabe
Der Parameter ist hier ein Dateiname! Die Datei enthält einen Text. Die Aufgabe des Skripts ist es zu entscheiden, welches Wort am häufigsten in anderen Wörtern vorkommt.
Beispiele für Eingabe und Ausgabe
(Beispiel: Der Text lautet: Ball spielen, Fußball, Basketball, Schneeball – deshalb ist der Ball der Gewinner, weil er zu drei anderen Welten gehört).
Mein Code bisher
Ich habe diesen Code bisher gemacht, aber er funktioniert nicht für jede Ausgabe
!/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" "
Antwort1
Wenn sich die Liste der Wörter in einer Datei namens befindet words
und jede Zeile ein einzelnes Wort enthält (möglicherweise erstellt mit , tr ' ' '\n' <originalwords >words
um die ursprüngliche Liste in mehrere Zeilen aufzuteilen), dann wird die Schleife
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 }'
gibt das Wort aus, das in der Liste am häufigsten vorkommt (oder, wenn viele Wörter gleich oft vorkommen, dasjenige der Wörter, das in der Liste zuerst vorkam).
Dies geschieht, indem die Liste selbst als Mustersatz verwendet wird, der mit der Liste abgeglichen wird. Mit -o
fordern wir die Rückgabe übereinstimmender Teilzeichenfolgen in einzelnen Zeilen an.
Die Ausgabe der Schleife allein mit der in der Frage angegebenen Liste lautet
play
ball
ball
ball
ball
football
basketball
snowball
Dann müssen Sie diese Wörter nur noch zählen und dasjenige heraussuchen, das am häufigsten vorkommt.
Als vollständiges Skript mit temporärer Dateiverwaltung:
#!/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 }'
Das Skript liest zusätzlich von der Standardeingabe, wenn keine Datei angegeben ist.
Antwort2
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
}'
Scannen Sie die Eingabe und extrahieren Sie eine Liste eindeutiger Wörter und eine Liste aller Wörter. (Ich vermute, wir brauchen die Liste eindeutiger Wörter nicht, aber bei einer großen Eingabe könnte sie die Arbeit effizienter machen.) Zählen Sie dann für jedes eindeutige Wort, wie viele Wörter in der Datei damit übereinstimmen. (Wenn die Datei also enthält football football football
, zählt das 3 für ball
.) Behalten Sie das Wort mit den meisten Übereinstimmungen im Auge.
Bei einem Gleichstand wird das Wort gemeldet, das als erstes im uwords
Array (eindeutige Wörter) erscheint. Dies ist nicht notwendigerweise das erste Wort, das in der Datei erscheint, und auch nicht das alphabetisch erste.
Dies kann zu unerwarteten Ergebnissen führen, wenn eines der Wörter.
, *
oder [
.
Wenn Sie den Shell+Awk-Ansatz von Kusalananda bevorzugen, aber den Grenzfallfehler vermeiden möchten, gehen Sie wie folgt vor:
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 }'
Durch das Sortieren der Wortliste erhalten wir eine Liste mit eindeutigen Wörtern und zählen somit keine Wörter mehrfach.
Beachten Sie, dass dieser Code explizit davon ausgeht, dass höchstens eine Eingabedatei vorhanden ist (es könnte aber auch keine Datei vorhanden sein, d. h., es wird von stdin gelesen). Dies entspricht der Formulierung der Frage. Wenn es jedoch eine beliebige Anzahl von Eingabedateien geben kann (null, eins,oder mehr), ändern Sie die tr
Zeile in
cat -- "$@" | tr -s ' ' '\n' > "$tmpfile" # convert into word list
Man könnte argumentieren, dass es sich um ein UUOC handelt, aber
- Es behandelt den Fall von zwei oder mehr Eingabedateien und
- es ist besser lesbar als
< "${1:-/dev/stdin}"
.