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?

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.


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]

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)}'

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

-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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.