web-dev-qa-db-fra.com

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

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, ceci ne trouve qu'une occurrence par ligne, en raison du fonctionnement de grep. Comment puis-je rechercher le nombre de fois qu'une chaîne apparaît dans un fichier, qu'ils soient sur la même ligne ou sur des lignes différentes?

De plus, que se passe-t-il si je recherche un motif regex, pas une simple chaîne? Comment puis-je les compter ou, mieux encore, imprimer chaque match sur une nouvelle ligne?

83
jrdioko

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

echo afoobarfoobar | grep -o foo | wc -l

Et man grep bien sûr (:

Mettre à jour

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

Ne pas.

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 correspond, le résultat est donc 1. En fait, -o est ignoré ici et vous pouvez simplement utiliser grep -c à la place.

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 -l ne compte que le nombre de lignes de la sortie.

143
hudolejev

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]
2
IBrewThereforeIAm

Un post tardif:
Utilisez le modèle regex de recherche comme séparateur d’enregistrements (RS) dans awk
Cela permet à votre expression rationnelle d’étendre sur __ lignes de code \n- (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)}'
1
Peter.O

Ripgrep , qui est une alternative rapide à grep, vient de présenter le drapeau --count-matches 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 autorise é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
0
Sebastian Müller