Contar el número de ocurrencias de un patrón en un archivo (incluso en la misma línea)

94

Cuando busco el número de ocurrencias de una cadena en un archivo, generalmente uso:

grep pattern file | wc -l

Sin embargo, esto solo encuentra una ocurrencia por línea, debido a la forma en que trabaja grep. ¿Cómo puedo buscar la cantidad de veces que aparece una cadena en un archivo, independientemente de si están en la misma línea o en líneas diferentes?

Además, ¿qué pasa si estoy buscando un patrón de expresiones regulares, no una simple cadena? ¿Cómo puedo contarlos o, mejor aún, imprimir cada coincidencia en una nueva línea?

jrdioko
fuente

Respuestas:

157

Para contar todas las ocurrencias, use -o. Prueba esto:

echo afoobarfoobar | grep -o foo | wc -l

Y man greppor supuesto (:

Actualizar

Algunos sugieren usar solo en grep -co foolugar de grep -o foo | wc -l.

No lo hagas.

Este atajo no funcionará en todos los casos. La página de manual dice:

-c print a count of matching lines

La diferencia en estos enfoques se ilustra a continuación:

1.

$ echo afoobarfoobar | grep -oc foo
1

Tan pronto como se encuentra la coincidencia en la línea ( a{foo}barfoobar), la búsqueda se detiene. Solo se verificó una línea y coincidió, por lo que la salida es 1. En realidad, -ose ignora aquí y podría usar grep -cen su lugar.

2.

$ echo afoobarfoobar | grep -o foo
foo
foo

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

Se encuentran dos coincidencias en la línea ( a{foo}bar{foo}bar) porque pedimos explícitamente encontrar cada ocurrencia ( -o). Cada ocurrencia se imprime en una línea separada y wc -lsolo cuenta el número de líneas en la salida.

hudolejev
fuente
1
Wow ... ¿es realmente así de simple?
jrdioko
1
grep -oc no funciona en este caso. Prueba echo afoobarfoobar | grep -oc foo
Paulus
¿No hay forma de hacer esto para varios archivos? Digamos que quiero ver el número de ocurrencias por archivo en un conjunto de archivos. Puedo hacerlo por línea con grep -c *, pero no por instancia.
Keith Tyler
grep -o foo a.txt b.txt | sort | uniq -cfunciona bien (con GNU grep): gist.github.com/hudolejev/81a05791f38cbacfd4de3ee3b44eb4f8
hudolejev
2

Prueba esto:

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

Muestra:

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]
IBrewPor lo tanto
fuente
1

Una publicación tardía:
use el patrón de expresión regular de búsqueda como un Separador de registros (RS) en awk
Esto permite que su expresión regular \nabarque líneas delimitadas (si lo necesita).

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

Ripgrep , que es una alternativa rápida a grep, acaba de introducir la --count-matchesbandera que permite contar cada coincidencia en la versión 0.9 (estoy usando el ejemplo anterior para mantener la coherencia):

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

Según lo solicitado por OP, ripgrep también permite el patrón de expresiones regulares ( --regexp <PATTERN>). También puede imprimir cada coincidencia (línea) en una línea separada:

> echo -e "line1foo\nline2afoobarfoobar" | rg foo
line1foo
line2afoobarfoobar
Sebastián Müller
fuente
-1

Hackea la función de color de grep y cuenta cuántas etiquetas de color imprime:

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
fuente