Comment compter le nombre d'occurrences d'un mot dans un fichier texte avec la ligne de commande?

43

J'ai un fichier JSON volumineux sur une ligne et je souhaite utiliser la ligne de commande pour pouvoir compter le nombre d'occurrences d'un mot dans le fichier. Comment puis je faire ça?

mythz
la source
Il n'est pas clair si le mot doit être mis en correspondance dans les clés et les valeurs des données JSON, c'est-à-dire s'il { "key": "the key" }doit compter la chaîne keyune ou deux fois.
Kusalananda

Réponses:

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

trremplace les espaces par des nouvelles lignes, grepfiltre toutes les lignes résultantes correspondant à WORD et wccompte les lignes restantes.

On peut même sauvegarder la wcpièce en utilisant l' -coption de grep:

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

L' -coption est définie par POSIX.

S'il n'est pas garanti qu'il y ait des espaces entre les mots, vous devez utiliser un autre caractère (comme délimiteur) à remplacer. Par exemple, des trpièces alternatives sont

tr '"' '\n'

ou

tr "'" '\n'

si vous souhaitez remplacer les guillemets simples ou doubles. Bien sûr, vous pouvez également utiliser trpour remplacer plusieurs caractères à la fois (pensez à différents types d'espaces et de ponctuation).

Si vous devez compter WORD mais pas prefixWORD, WORDsuffix ou prefixWORDsuffix, vous pouvez inclure le modèle WORD dans des marqueurs de début / fin de ligne:

grep -c '^WORD$'

Ce qui équivaut à des marqueurs de début / fin de mot, dans notre contexte:

grep -c '\<WORD\>'
maxschlepzig
la source
que se passe-t-il s'il n'y a pas d'espaces, c'est-à-dire que le nom du champ est entouré de guillemets? par exemple "terrain"
mythz
@mythz: Ensuite, vous remplacez les guillemets par des nouvelles lignes par tr. Je vais mettre à jour la réponse.
maxschlepzig le
1
Cette réponse est incorrecte à bien des égards. C'est vague: vous devez expliquer comment créer une trcommande qui fait le travail au lieu de suggérer des exemples qui ne fonctionneront jamais dans toutes les situations. Cela fera aussi correspondre les mots qui contiennent le mot que vous recherchez. La grep -o '\<WORD\>' | wc -lsolution est de loin supérieure.
Sam Hocevar
1
@Sam, la question laisse en quelque sorte ouverte, si un mot recherché doit être recherché comme 'WORD' ou '\ <WORD \>' - vous pouvez le lire dans les deux sens. Même si vous le lisez de la deuxième manière et seulement de la deuxième, ma réponse ne serait incorrecte que dans un sens. ;) Et la solution 'grep -o' n'est supérieure que si elle supporte l'option -o - qui n'est pas spécifiée par POSIX ... Bien, je ne pense pas que l'utilisation de tr soit aussi exotique que de l'appeler vague ...
maxschlepzig
1
@ Kusalananda, eh bien, c'est toujours un événement. Mais si vous ne voulez pas compter ces correspondances de sous-chaînes, veuillez lire le dernier paragraphe de ma réponse et mon commentaire précédent ici.
maxschlepzig le
24

Avec GNU grep, cela fonctionne: grep -o '\<WORD\>' | wc -l

-o imprime chaque partie correspondante de chaque ligne sur une ligne distincte.

\<affirme le début d'un mot et \>la fin d'un mot (similaire à celui de Perl \b), ce qui garantit que vous ne faites pas correspondre une chaîne au milieu d'un mot.

Par exemple,

$ python -c 'importer cette' | grep '\ <one \>'
Il devrait y avoir un moyen - et de préférence un seul - évident de le faire.
Les espaces de noms sont une idée géniale - faisons-en plus!
$ python -c 'importer cette' | grep -o '\ <one \>'
 one 
one 
one 
$ python -c 'importer cette' | grep -o '\ <one \>' | wc -l
3
éphémère
la source
1
Ou tout simplementgrep -wo WORD | wc -l
Stéphane Chazelas
10

Cela ne fonctionne malheureusement pas avec GNU coreutils.

grep -o -c WORD file

Si cela fonctionne sur votre plate-forme, c'est une solution élégante et assez intuitive; mais les gens de GNU réfléchissent encore.

triple
la source
2
Mon diable, le bogue est toujours ouvert: savannah.gnu.org/bugs/?33080
triplee
1
Dommage que cela aurait été le plus élégant
MasterScrat
Cela a fonctionné pour moi!
ThisaruG
C'est faux. Ceci compte le nombre de lignes avec le motif WORD. Le PO veut le nombre total d’occurrences.
Pierre B
@PierreB C'est pourquoi je dis que GNU grepa un bug ici. POSIX n’indique pas clairement ce -cque -odevrait être la sémantique de la combinaison , ce qui n’est pour le moment pas portable. Merci pour le commentaire; J'ai mis à jour cette réponse.
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 

Cette commande a les effets suivants:

  1. Remplacez tous les caractères non alphanumériques par un espace.
  2. Tous les sauts de ligne sont également convertis en espaces.
  3. Réduit tous les espaces vides en un seul.
  4. Tous les espaces sont maintenant convertis en sauts de ligne. Chaque mot dans une ligne.
  5. Traduit tous les mots en minuscules pour éviter que 'Hello' et 'hello' soient des mots différents
  6. Sorts de text
  7. Compte et enlève les lignes égales
  8. Trie en sens inverse pour compter les mots les plus fréquents
  9. Ajouter un numéro de ligne à chaque mot afin de connaître la posotion du mot dans son ensemble

Par exemple, si je veux analyser le premier message de Linus Torvald:

De: [email protected] (Linus Benedict Torvalds) Groupes de discussion: comp.os.minix Objet: Que voudriez-vous voir le plus dans minix? Résumé: petit sondage pour mon nouveau système d'exploitation Message-ID: <[email protected]> Date: 25 août 91 20:57:08 GMT Organisation: Université de Helsinki

Bonjour à tous, utilisez minix -

Je fais un système d'exploitation (gratuit) (juste un passe-temps, ne sera pas grand et professionnel comme gnu) pour 386 (486) clones. Cela se prépare depuis avril et commence à se préparer. Je souhaite tout retour sur les choses que les gens aiment / n'aiment pas dans minix, car mon système d'exploitation lui ressemble un peu (même disposition physique du système de fichiers (pour des raisons pratiques), entre autres choses).

J'ai actuellement porté bash (1.08) et gcc (1.40), et les choses semblent fonctionner. Cela implique que j'aurai quelque chose de concret dans quelques mois et j'aimerais savoir quelles fonctionnalités la plupart des gens souhaiteraient. Toutes les suggestions sont les bienvenues, mais je ne promets pas que je les appliquerai

Linus ([email protected])

PS Oui, il ne contient aucun code minix, et il a un fs multi-threadé. Il n'est PAS protable (utilise 386 tâches, etc.), et il ne supportera probablement jamais rien d'autre que les disques durs AT, car c'est tout ce que j'ai :-(.

Je crée un fichier nommé linus.txt , je colle le contenu puis j'écris dans la console:

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

Le résultat serait:

 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        ...

Si vous voulez visualiser uniquement les 20 premiers mots:

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

Est important de noter que la commande tr « AZ » « a-z » ne suport UTF-8 encore , de sorte que dans les langues étrangères seraient traduites mot comme après APRÈS.

Si vous voulez seulement rechercher l'occurrence d'un mot, vous pouvez ajouter un grep à la fin:

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$"

Dans un script appelé 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$"

Le script doit s'appeler:

 search_freq word_to_search_for
Roger Borrell
la source
sed: -e expression #2, char 7: unterminated s 'commande`, cela compte aussi tous les mots, non? Mais OP a demandé seulement un particulier. Aussi un peu d'explication serait bien.
phk
Désolé j'ai eu une erreur. J'ai refait la commande et commenté la réponse. À mon avis, d'après la question, il est impossible de savoir s'il aimerait obtenir la simultanéité d'un seul mot ou la fréquence d'occurrences. Mais si vous souhaitez ne recevoir qu'un mot, vous pouvez ajouter un grep à la fin.
Roger Borrell
3

Selon que vous souhaitez faire correspondre le mot dans les clés ou dans les valeurs des données JSON, vous souhaiterez probablement extraire uniquement les clés ou les valeurs des données. Sinon, vous risquez de compter trop de mots si ils apparaissent à la fois comme clés et comme valeurs.

Pour extraire toutes les clés:

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

Ceci teste de manière récursive si la chose actuelle est un objet et si c'est le cas, il extrait les clés. La sortie sera une liste de clés, une par ligne.

Pour extraire toutes les valeurs:

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

Cela fonctionne de la même manière, mais en moins d’étapes.

Vous pouvez ensuite diriger la sortie de ce qui précède grep -c 'PATTERN'(pour faire correspondre certains motifs aux clés ou aux valeurs), ou grep -c -w -F 'WORD'(pour faire correspondre un mot aux clés ou aux valeurs), ou grep -c -x -F 'WORD'(pour faire correspondre une clé ou une valeur complète), ou similaire, à faites votre compte.

Kusalananda
la source
0

J'ai json avec quelque chose comme ça: "number":"OK","number":OK"répété plusieurs fois dans une ligne.

Mon simple compteur "OK":

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

khazad-dum_miner
la source
-1

J'ai utilisé la commande ci-dessous awk pour trouver le nombre d'occurrences

exemple de fichier

cat file1

praveen ajay 
praveen
ajay monkey praveen
praveen boy praveen

commander:

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

sortie

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

5
Praveen Kumar BS
la source
Ou juste awk '{sum+=gsub("praveen","")} END {print sum+0}'.
G-Man dit 'Réintégrez Monica'
Laissez-moi savoir pourquoi voter pour ma réponse
Praveen Kumar BS