Compter le nombre d'occurrences d'un motif dans un fichier (même sur la même ligne)

94

Lors de la recherche du nombre d'occurrences d'une chaîne dans un fichier, j'utilise généralement:

grep pattern file | wc -l

Cependant, cela ne trouve qu'une occurrence par ligne, en raison de la façon dont fonctionne grep. Comment puis-je rechercher le nombre de fois qu'une chaîne apparaît dans un fichier, qu'elles soient sur les mêmes lignes ou sur des lignes différentes?

Aussi, que faire si je recherche un modèle regex, pas une simple chaîne? Comment puis-je les compter ou, mieux encore, imprimer chaque correspondance sur une nouvelle ligne?

jrdioko
la source

Réponses:

156

Pour compter toutes les occurrences, utilisez -o. Essaye ça:

echo afoobarfoobar | grep -o foo | wc -l

Et man grepbien sûr (:

Mettre à jour

Certains suggèrent d'utiliser juste grep -co fooau lieu de grep -o foo | wc -l.

Ne fais pas ça.

Ce raccourci ne fonctionnera pas dans tous les cas. La page de manuel dit:

-c print a count of matching lines

La différence entre ces approches est illustrée ci-dessous:

1.

$ echo afoobarfoobar | grep -oc foo
1

Dès que la correspondance est trouvée dans la ligne ( a{foo}barfoobar), la recherche s'arrête. Une seule ligne a été vérifiée et correspondait, donc la sortie est 1. En fait, il -oest ignoré ici et vous pouvez simplement l'utiliser à la grep -cplace.

2.

$ echo afoobarfoobar | grep -o foo
foo
foo

$ echo afoobarfoobar | grep -o foo | wc -l
2

Deux correspondances sont trouvées dans la ligne ( a{foo}bar{foo}bar) car nous avons explicitement demandé de trouver chaque occurrence ( -o). Chaque occurrence est imprimée sur une ligne distincte et wc -lne compte que le nombre de lignes dans la sortie.

hudolejev
la source
1
Wow ... est-ce vraiment aussi simple que ça?
jrdioko
1
grep -oc ne fonctionne pas dans ce cas. Essayez echo afoobarfoobar | grep -oc foo
Paulus
N'existe-t-il aucun moyen de le faire pour plusieurs fichiers? Disons que je veux voir le nombre d'occurrences par fichier sur un ensemble de fichiers. Je peux le faire par ligne avec grep -c *, mais pas par instance.
Keith Tyler
grep -o foo a.txt b.txt | sort | uniq -cfonctionne très bien (avec GNU grep): gist.github.com/hudolejev/81a05791f38cbacfd4de3ee3b44eb4f8
hudolejev
2

Essaye ça:

grep "string to search for" FileNameToSearch | cut -d ":" -f 4 | sort -n | uniq -c

Échantillon:

grep "SMTP connect from unknown" maillog | cut -d ":" -f 4 | sort -n | uniq -c
  6  SMTP connect from unknown [188.190.118.90]
 54  SMTP connect from unknown [62.193.131.114]
  3  SMTP connect from unknown [91.222.51.253]
IBrewThereforeIAm
la source
1

Un article tardif:
utilisez le modèle de recherche regex comme séparateur d'enregistrement (RS) dans awk
Cela permet à votre expression \nrégulière de s'étendre sur des lignes délimitées (si vous en avez besoin).

printf 'X \n moo X\n XX\n' | 
   awk -vRS='X[^X]*X' 'END{print (NR<2?0:NR-1)}'
Peter.O
la source
0

Ripgrep , qui est une alternative rapide à grep, vient d'introduire le --count-matchesdrapeau permettant de compter chaque correspondance dans la version 0.9 (j'utilise l'exemple ci-dessus pour rester cohérent):

> echo afoobarfoobar | rg --count foo
1
> echo afoobarfoobar | rg --count-matches foo
2

Comme demandé par OP, ripgrep permet également le motif regex ( --regexp <PATTERN>). Il peut également imprimer chaque correspondance (ligne) sur une ligne distincte:

> echo -e "line1foo\nline2afoobarfoobar" | rg foo
line1foo
line2afoobarfoobar
Sebastian Müller
la source
-1

Hackez la fonction de couleur de grep et comptez le nombre de balises de couleur imprimées:

echo -e "a\nb  b b\nc\ndef\nb e brb\nr" \
| GREP_COLOR="033" grep --color=always  b \
| perl -e 'undef $/; $_=<>; s/\n//g; s/\x1b\x5b\x30\x33\x33/\n/g; print $_' \
| wc -l
Shizzmo
la source