Contagem do número total de ocorrências usando grep

171

grep -c é útil para descobrir quantas vezes uma string ocorre em um arquivo, mas só conta cada ocorrência uma vez por linha. Como contar múltiplas ocorrências por linha?

Estou procurando algo mais elegante do que:

perl -e '$_ = <>; print scalar ( () = m/needle/g ), "\n"'
    
por 030 06.02.2011 / 16:21

6 respostas

246

O -o do grep só produzirá as correspondências, ignorando as linhas; wc pode contá-los:

grep -o 'needle' file | wc -l

Isso também corresponderá a 'needles' ou 'multineedle'.
Apenas palavras únicas:

grep -o '\bneedle\B' file | wc -l
# or:
grep -o '\<needle\>' file | wc -l
    
por 06.02.2011 / 17:27
14

Se você tem o GNU grep (sempre no Linux e Cygwin, ocasionalmente em outro lugar), você pode conte as linhas de saída de grep -o : grep -o needle | wc -l .

Com o Perl, aqui estão algumas maneiras que eu acho mais elegantes do que as suas (mesmo depois que ele é fixo ).

perl -lne 'END {print $c} map ++$c, /needle/g'
perl -lne 'END {print $c} $c += s/needle//g'
perl -lne 'END {print $c} ++$c while /needle/g'

Com apenas ferramentas POSIX, uma abordagem, se possível, é dividir a entrada em linhas com uma única correspondência antes de passá-la ao grep. Por exemplo, se você está procurando palavras inteiras, primeiro transforme cada caractere não pertencente à palavra em uma nova linha.

# equivalent to grep -ow 'needle' | wc -l
tr -c '[:alnum:]' '[\n*]' | grep -c '^needle$'

Caso contrário, não há um comando padrão para fazer esse processamento específico de texto, então você precisa ativar o sed (se você é masoquista) ou awk.

awk '{while (match($0, /set/)) {++c; $0=substr($0, RSTART+RLENGTH)}}
     END {print c}'
sed -n -e 's/set/\n&\n/g' -e 's/^/\n/' -e 's/$/\n/' \
       -e 's/\n[^\n]*\n/\n/g' -e 's/^\n//' -e 's/\n$//' \
       -e '/./p' | wc -l

Aqui está uma solução mais simples usando sed e grep , que funciona para cadeias de caracteres ou até mesmo para expressões regulares, mas falha em alguns casos com padrões ancorados (por exemplo, encontra duas ocorrências de ^needle ou \bneedle em needleneedle ).

sed 's/needle/\n&\n/g' | grep -cx 'needle'

Observe que nas substituições do sed acima, usei \n para significar uma nova linha. Isso é padrão na parte padrão, mas no texto de substituição, para portabilidade, substitua a barra invertida-newline por \n .

    
por 15.05.2011 / 14:36
3

Se, como eu, você realmente queria "ambos; cada um exatamente uma vez", (na verdade, "duas vezes"), é simples:

grep -E "thing1|thing2" -c

e verifique a saída 2 .

O benefício dessa abordagem (se exatamente uma vez é o que você quer) é que ela é facilmente dimensionada.

    
por 13.01.2017 / 14:20
2

Outra solução usando awk e needle como separador de campo:

awk -F'^needle | needle | needle$' '{c+=NF-1}END{print c}'

Se você deseja corresponder needle seguido de pontuação, altere o separador de campo de acordo, ou seja,

awk -F'^needle[ ,.?]|[ ,.?]needle[ ,.?]|[ ,.?]needle$' '{c+=NF-1}END{print c}'

Ou use a classe: [^[:alnum:]] para incluir todos os caracteres não alfa.

    
por 15.05.2011 / 15:54
1

Seu exemplo só exibe o número de ocorrências por linha e não o total no arquivo. Se é isso que você quer, algo assim pode funcionar:

perl -nle '$c+=scalar(()=m/needle/g);END{print $c}' 
    
por 06.02.2011 / 16:41
1

Esta é a minha pura solução bash

#!/bin/bash

B=$(for i in $(cat /tmp/a | sort -u); do
echo "$(grep $i /tmp/a | wc -l) $i"
done)

echo "$B" | sort --reverse
    
por 08.08.2012 / 23:31

Tags