Contar o número de ocorrências de um padrão em um arquivo (mesmo na mesma linha)

94

Ao pesquisar o número de ocorrências de uma string em um arquivo, geralmente uso:

grep pattern file | wc -l

No entanto, isso só encontra uma ocorrência por linha, devido à maneira como o grep funciona. Como posso pesquisar o número de vezes que uma string aparece em um arquivo, independentemente de estarem na mesma linha ou em linhas diferentes?

Além disso, e se eu estiver procurando um padrão regex, não uma string simples? Como posso contá-los ou, melhor ainda, imprimir cada correspondência em uma nova linha?

Jrdioko
fonte

Respostas:

156

Para contar todas as ocorrências, use -o. Experimente isto:

echo afoobarfoobar | grep -o foo | wc -l

E man grepclaro (:

Atualizar

Alguns sugerem usar apenas em grep -co foovez de grep -o foo | wc -l.

Não.

Este atalho não funcionará em todos os casos. A página do manual diz:

-c print a count of matching lines

A diferença nessas abordagens é ilustrada abaixo:

1

$ echo afoobarfoobar | grep -oc foo
1

Assim que a correspondência for encontrada na linha ( a{foo}barfoobar), a pesquisa será interrompida. Apenas uma linha foi verificada e correspondeu, então a saída é 1. Na verdade, -oé ignorado aqui e você pode apenas usar em seu grep -clugar.

2

$ echo afoobarfoobar | grep -o foo
foo
foo

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

Duas correspondências são encontradas na linha ( a{foo}bar{foo}bar) porque pedimos explicitamente para encontrar todas as ocorrências ( -o). Cada ocorrência é impressa em uma linha separada e wc -lconta apenas o número de linhas na saída.

Hudolejev
fonte
1
Uau ... é realmente tão simples?
Jrdioko
1
grep -oc não funciona neste caso. Experimente echo afoobarfoobar | grep -oc foo
Paulus de
Não há como fazer isso com vários arquivos? Digamos que eu queira ver o número de ocorrências por arquivo em um conjunto de arquivos. Posso fazer isso por linha com grep -c *, mas não por instância.
Keith Tyler
grep -o foo a.txt b.txt | sort | uniq -cfunciona bem (com GNU grep): gist.github.com/hudolejev/81a05791f38cbacfd4de3ee3b44eb4f8
hudolejev
2

Experimente isto:

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

Amostra:

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
fonte
1

Uma postagem atrasada:
Use o padrão de pesquisa regex como um Separador de Registro (RS) em awk
Isso permite que sua regex \nabranja linhas delimitadas (se necessário).

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

Ripgrep , que é uma alternativa rápida para grep, acaba de introduzir o --count-matchessinalizador que permite contar cada correspondência na versão 0.9 (estou usando o exemplo acima para permanecer consistente):

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

Conforme solicitado pelo OP, ripgrep permite o padrão regex também ( --regexp <PATTERN>). Também pode imprimir cada correspondência (linha) em uma linha separada:

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

Hackeie a função de cor do grep e conte quantas marcas de cor ele 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
fonte