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
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ê precisasort
antes de alimentaruniq
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.
grep
é especificado, mas para qualquer pessoa que useack
, a resposta é simplesmenteack -ch <pattern>
.