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 várias ocorrências por linha?

Estou procurando algo mais elegante do que:

perl -e "$_ = <>; print scalar ( () = m/needle/g ), "\n"" 

Comentários

  • Eu sei que grep é especificado, mas para qualquer pessoa que use ack, a resposta é simplesmente ack -ch <pattern>.
  • @KyleStrand Para mim ack -ch < padrão > contou apenas as linhas com ocorrências e não o número de ocorrências
  • @MarcKees Olhando para a página do manual, parece ser o comportamento correto. Obrigado por apontar isso!

Resposta

grep “s -o exibirá apenas as correspondências, ignorando as linhas; wc pode contá-los:

grep -o "needle" file | wc -l 

Isso também corresponderá a “agulhas” ou “multineedle”.

Para combinar apenas palavras únicas, use um dos seguintes comandos:

grep -ow "needle" file | wc -l grep -o "\bneedle\b" file | wc -l grep -o "\<needle\>" file | wc -l 

Comentários

  • Observe que isso requer GNU grep (Linux, Cygwin, FreeBSD, OSX).
  • @wag Que mágica faz \b e \B faz aqui?
  • @Geek \ b corresponde a um limite de palavra, \ B corresponde a NÃO um limite de palavra. A resposta acima seria mais correta se usasse \ b em ambas as extremidades.
  • Para uma contagem de ocorrências por linha, combine com a opção grep -n e uniq -c … grep -no ' \ < agulha \ > ' arquivo | uniq -c
  • @jameswarren uniq remove apenas linhas idênticas adjacentes, você precisa sort antes de alimentar uniq se você ainda não tiver certeza de que as duplicatas sempre estarão imediatamente adjacentes.

Resposta

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

Com Perl, aqui estão algumas maneiras que considero mais elegantes do que a sua (mesmo depois de “s fixed ).

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 o insira em linhas com uma única correspondência antes de passá-lo para grep. Por exemplo, se você estiver procurando palavras inteiras, primeiro transforme cada caractere não-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 isso é parte específica do processamento de texto, então você precisa recorrer ao sed (se você for um 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 “uma solução mais simples usando sed e grep, que funciona para strings ou mesmo expressões regulares do livro, mas falha em alguns casos extremos com padrões ancorados ( por exemplo ele 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 do padrão, mas no texto de substituição, para portabilidade, substitua barra invertida-nova linha por \n.

Resposta

Se, como eu, você realmente quisesse “ambos; cada um exatamente uma vez”, (na verdade é “qualquer um; duas vezes”) então é simples :

grep -E "thing1|thing2" -c 

e verifique a saída 2.

O benefício desta abordagem (se exatamente uma vez for o que você deseja) é que ele pode ser escalado facilmente.

Comentários

  • I ' não tenho certeza se você ' está realmente verificando ' está aparecendo apenas uma vez? Todos vocês ' procurando se uma dessas palavras existe pelo menos uma vez.
  • Esta deve ser a resposta aceita. Não há necessidade de usar wc -l, grep tem uma opção embutida para contar coisas e até é chamada de óbvia como -c para “contar”!

Resposta

Outro 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 abranger todos os caracteres não alfa.

Comentários

  • Observe que isso requer um awk que suporte separadores de campo regexp (como GNU awk).

Resposta

Esta é minha solução bash pura

#!/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 

Resposta

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

perl -nle "$c+=scalar(()=m/needle/g);END{print $c}" 

Comentários

  • Você estão certos – meu exemplo conta apenas as ocorrências na primeira linha.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *