Wie zähle ich die Anzahl der Vorkommen eines Wortes in einer Textdatei mit der Befehlszeile?

43

Ich habe eine große JSON-Datei, die sich in einer Zeile befindet, und ich möchte die Befehlszeile verwenden, um die Anzahl der Vorkommen eines Wortes in der Datei zu zählen. Wie kann ich das machen?

Mythos
quelle
Es ist unklar, ob das Wort in beiden Schlüsseln und Werten der JSON-Daten übereinstimmen soll, dh ob { "key": "the key" }die Zeichenfolge keyein- oder zweimal gezählt werden soll.
Kusalananda

Antworten:

45
$ tr ' ' '\n' < FILE | grep WORD | wc -l

Wenn trLeerzeichen durch Zeilenumbrüche ersetzt werden, werden grepalle resultierenden Zeilen, die mit WORD übereinstimmen, gefiltert und wcdie verbleibenden Zeilen gezählt .

Man kann das wcTeil sogar mit der -cOption grep speichern :

$ tr ' ' '\n' < FILE | grep -c WORD

Die -cOption wird von POSIX definiert.

Wenn nicht garantiert wird, dass zwischen den Wörtern Leerzeichen stehen, müssen Sie ein anderes Zeichen (als Trennzeichen) verwenden, um diese zu ersetzen. Zum Beispiel sind alternative trTeile

tr '"' '\n'

oder

tr "'" '\n'

Wenn Sie doppelte oder einfache Anführungszeichen ersetzen möchten. Natürlich können Sie auch trmehrere Zeichen gleichzeitig ersetzen (verschiedene Arten von Leerzeichen und Interpunktionszeichen).

Falls Sie WORD, aber nicht prefixWORD, WORDsuffix oder prefixWORDsuffix zählen müssen, können Sie das WORD-Muster in Zeilenanfangs- / Zeilenende-Markierungen einschließen:

grep -c '^WORD$'

Was in unserem Kontext den Wortanfangs- / -ende-Markierungen entspricht:

grep -c '\<WORD\>'
maxschlepzig
quelle
Was ist, wenn keine Leerzeichen vorhanden sind, dh der Feldname von Anführungszeichen umgeben ist? zB "Feld"
Mythos
@mythz: Dann ersetzen Sie die Anführungszeichen durch Zeilenumbrüche mit tr. Ich werde die Antwort aktualisieren.
Maxschlepzig
1
Diese Antwort ist in vielerlei Hinsicht falsch. Es ist vage: Sie sollten erklären, wie Sie einen trBefehl erstellen, der die Aufgabe erfüllt, anstatt Beispiele vorzuschlagen, die niemals in allen Situationen funktionieren. Es werden auch Wörter gefunden, die das gesuchte Wort enthalten. Die grep -o '\<WORD\>' | wc -lLösung ist weit überlegen.
Sam Hocevar
1
@Sam, die Frage lässt es offen, ob ein gesuchtes Wort wie 'WORT' oder '\ <WORT \>' gesucht werden soll - Sie können es in beide Richtungen lesen. Selbst wenn Sie es auf die 2. und nur auf die 2. Weise lesen, wäre meine Antwort nur auf eine Weise falsch. ;) Und die 'grep -o'-Lösung ist nur dann überlegen, wenn sie die -o-Option unterstützt - die von POSIX nicht angegeben wird ... Nun, ich denke nicht, dass die Verwendung von tr so exotisch ist, um es zu nennen vage ...
maxschlepzig
1
@ Kusalananda, nun, es ist immer noch ein Ereignis. Aber wenn Sie solche Teilstring-Übereinstimmungen nicht zählen möchten, lesen Sie bitte den letzten Absatz meiner Antwort und meinen vorherigen Kommentar hier.
Maxschlepzig
24

Mit GNU grep funktioniert Folgendes: grep -o '\<WORD\>' | wc -l

-o druckt alle übereinstimmenden Teile jeder Zeile in eine separate Zeile.

\<Setzt den Anfang eines Wortes und \>das Ende eines Wortes (ähnlich wie bei Perl \b). Dadurch wird sichergestellt, dass Sie keine Zeichenfolge in der Mitte eines Wortes finden.

Zum Beispiel,

$ python -c 'importiere dies' | grep '\ <one \>'
Es sollte einen - und am besten nur einen - offensichtlichen Weg geben, dies zu tun.
Namespaces sind eine großartige Idee - machen wir mehr davon!
$ python -c 'importiere dies' | grep -o '\ <one \>'
 one 
one 
one 
$ python -c 'importiere dies' | grep -o '\ <one \>' | wc -l
3
vergänglich
quelle
1
Oder einfachgrep -wo WORD | wc -l
Stéphane Chazelas
10

Dies funktioniert leider nicht mit GNU coreutils.

grep -o -c WORD file

Wenn es auf Ihrer Plattform funktioniert, ist es eine elegante und recht intuitive Lösung. Aber die GNU-Leute denken immer noch nach.

Tripleee
quelle
2
Mein schlecht, ist der Fehler noch offen: savannah.gnu.org/bugs/?33080
tripleee
1
Schade, dass dies die eleganteste gewesen wäre
MasterScrat
Das hat bei mir funktioniert!
Heute,
Das ist falsch. Dies zählt die Anzahl der Zeilen mit dem Muster WORT. Das OP möchte die Gesamtzahl der Vorkommen.
Pierre B
@PierreB Deshalb sage ich, dass GNU grephier einen Fehler hat. Es ist aus POSIX nicht ersichtlich, welche Semantik beim Kombinieren zu beachten ist -cund -osollte, sodass dies derzeit nicht portierbar ist. Danke für den Kommentar; Ich habe diese Antwort aktualisiert.
Tripleee
7
sed -e 's/[^[:alpha:]]/ /g' text_to_analize.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl 

Dieser Befehl führt Folgendes aus:

  1. Ersetzen Sie alle nicht alphanumerischen Zeichen durch ein Leerzeichen.
  2. Alle Zeilenumbrüche werden auch in Leerzeichen umgewandelt.
  3. Reduziert alle mehreren Leerzeichen auf ein Leerzeichen
  4. Alle Leerzeichen werden jetzt in Zeilenumbrüche umgewandelt. Jedes Wort in einer Zeile.
  5. Übersetzt alle Wörter in Kleinbuchstaben, um zu vermeiden, dass "Hallo" und "Hallo" unterschiedliche Wörter sind
  6. Sortiert den Text
  7. Zählt und entfernt die gleichen Linien
  8. Sortiert umgekehrt, um die häufigsten Wörter zu zählen
  9. Fügen Sie jedem Wort eine Zeilennummer hinzu, um die Position des Wortes im Ganzen zu kennen

Zum Beispiel, wenn ich die erste Linus Torvald-Nachricht analysieren möchte:

Von: [email protected] (Linus Benedict Torvalds) Newsgroups: comp.os.minix Betreff: Was möchten Sie am liebsten in minix sehen? Zusammenfassung: Kleine Umfrage für mein neues Betriebssystem Message-ID: <[email protected]> Datum: 25. August 91 20:57:08 GMT Organisation: Universität Helsinki

Hallo allerseits mit minix -

Ich mache ein (freies) Betriebssystem (nur ein Hobby, werde nicht so groß und professionell sein wie Gnu) für 386 (486) AT-Klone. Dieses braut seit April und fängt an, fertig zu werden. Ich hätte gerne Feedback zu Dingen, die Leute in Minix mögen / nicht mögen, da mein Betriebssystem dem etwas ähnelt (gleiche physische Anordnung des Dateisystems (unter anderem aus praktischen Gründen)).

Ich habe derzeit bash (1.08) und gcc (1.40) portiert, und die Dinge scheinen zu funktionieren. Dies impliziert, dass ich in ein paar Monaten etwas Praktisches bekomme und ich würde gerne wissen, welche Funktionen die meisten Leute möchten. Vorschläge sind willkommen, aber ich verspreche nicht, dass ich sie umsetzen werde 🙂

Linus ([email protected])

PS. Ja - es ist frei von jeglichem Minix-Code und es hat ein Multi-Threaded-Fs. Es ist NICHT protable (verwendet 386 Task Switching usw.), und es wird wahrscheinlich nie etwas anderes als AT-Festplatten unterstützen, da das alles ist, was ich habe :-(.

Ich erstelle eine Datei mit dem Namen linus.txt , füge den Inhalt ein und schreibe dann in die Konsole:

sed -e 's/[^[:alpha:]]/ /g' linus.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl 

Die Ausgabe wäre:

 1        7 i
 2        5 to
 3        5 like
 4        5 it
 5        5 and
 6        4 minix
 7        4 a
 8        3 torvalds
 9        3 of
10        3 helsinki
11        3 fi
12        3 any
13        2 would
14        2 won
15        2 what
16        ...

Wenn Sie nur die ersten 20 Wörter anzeigen möchten:

sed -e 's/[^[:alpha:]]/ /g' text_to_analize.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl | head -n 20

Es ist wichtig zu beachten, dass der Befehl tr 'AZ' 'a-z' UTF-8 noch nicht unterstützt , sodass das Wort APRÈS in Fremdsprachen als AprÈs übersetzt wird.

Wenn Sie nur nach dem Vorkommen eines Wortes suchen möchten, können Sie am Ende ein Grep einfügen:

sed -e 's/[^[:alpha:]]/ /g' text_to_analize.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl | grep "\sword_to_search_for$"

In einem Skript namens search_freq :

#!/bin/bash
sed -e 's/[^[:alpha:]]/ /g' text_to_analize.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl | grep "\s$1$"

Das Skript muss aufgerufen werden:

 search_freq word_to_search_for
Roger Borrell
quelle
sed: -e expression #2, char 7: unterminated s "Befehl", auch das zählt alle Wörter, oder? Aber OP fragte nur einen bestimmten. Auch ein bisschen Erklärung wäre schön.
Phk
Entschuldigung, ich hatte einen Fehler. Ich habe den Befehl neu gemacht und die Antwort kommentiert. Meiner Meinung nach ist es aus der Frage unmöglich zu wissen, ob er die Okurrency von nur einem Wort oder einer Häufigkeit von Vorkommen erhalten möchte. Wenn Sie jedoch nur ein Wort erhalten möchten, können Sie am Ende ein Grep einfügen.
Roger Borrell
3

Je nachdem, ob Sie das Wort in den Schlüsseln oder in den Werten der JSON-Daten abgleichen möchten, möchten Sie wahrscheinlich nur Schlüssel oder nur Werte aus den Daten extrahieren. Andernfalls können Sie einige Wörter zu oft zählen, wenn sie sowohl als Schlüssel als auch als Werte vorkommen.

So extrahieren Sie alle Schlüssel:

jq -r '..|objects|keys[]' <file.json

Dies prüft rekursiv, ob das aktuelle Objekt ein Objekt ist, und wenn dies der Fall ist, werden die Schlüssel extrahiert. Die Ausgabe ist eine Liste von Schlüsseln, eine pro Zeile.

So extrahieren Sie alle Werte:

jq -r '..|scalars' <file.json

Dies funktioniert auf ähnliche Weise, hat jedoch weniger Schritte.

Sie können dann die Ausgabe des obigen Befehls durch grep -c 'PATTERN'(um ein Muster mit den Schlüsseln oder Werten abzugleichen), oder grep -c -w -F 'WORD'(um ein Wort in den Schlüsseln oder Werten abzugleichen) oder grep -c -x -F 'WORD'(um einen vollständigen Schlüssel oder Wert abzugleichen) oder Ähnliches zu leiten Zähle.

Kusalananda
quelle
0

Ich habe Json mit so etwas: "number":"OK","number":OK"mehrmals in einer Zeile wiederholt.

Mein einfacher "OK" Zähler:

sed "s|,|\n|g" response | grep -c OK

khazad-dum_miner
quelle
-1

Ich habe unter awk Befehl verwendet, um die Anzahl der Vorkommen zu finden

Beispieldatei

Katzendatei1

praveen ajay 
praveen
ajay monkey praveen
praveen boy praveen

Befehl:

awk '{print gsub("praveen",$0)}' file1 | awk 'BEGIN{sum=0}{sum=sum+$1}END{print sum}'

Ausgabe

awk '{print gsub("praveen",$0)}' file1 | awk 'BEGIN{sum=0}{sum=sum+$1}END{print sum}'

5
Praveen Kumar BS
quelle
Oder einfach awk '{sum+=gsub("praveen","")} END {print sum+0}'.
G-Man sagt, dass Monica
Lassen Sie mich wissen, warum ich für meine Antwort gestimmt habe
Praveen Kumar BS