Como posso contar o número de ocorrências de uma palavra em um arquivo de texto com a linha de comando?

43

Eu tenho um arquivo JSON grande que está em uma linha e quero usar a linha de comando para poder contar o número de ocorrências de uma palavra no arquivo. Como eu posso fazer isso?

mythz
fonte
Não está claro se a palavra deve corresponder nas chaves e nos valores dos dados JSON, ou seja, se { "key": "the key" }deve contar a sequência keyuma ou duas vezes.
Kusalananda

Respostas:

46
$ tr ' ' '\n' < FILE | grep WORD | wc -l

Onde trsubstitui espaços por novas linhas, grepfiltra todas as linhas resultantes que correspondem a WORD e wcconta as restantes.

Pode-se até salvar a wcpeça usando a -copção grep:

$ tr ' ' '\n' < FILE | grep -c WORD

A -copção é definida pelo POSIX.

Se não for garantido que haja espaços entre as palavras, você precisará usar outro caractere (como delimitador) para substituir. Por exemplo, trpeças alternativas são

tr '"' '\n'

ou

tr "'" '\n'

se você deseja substituir aspas simples ou duplas. Obviamente, você também pode usar trpara substituir vários caracteres ao mesmo tempo (pense em diferentes tipos de espaço em branco e pontuação).

Caso você precise contar WORD, mas não o prefixoWORD, WORDsuffix ou prefixWORDsuffix, é possível incluir o padrão WORD nos marcadores de início / fim de linha:

grep -c '^WORD$'

O que é equivalente aos marcadores de início / fim de palavra, em nosso contexto:

grep -c '\<WORD\>'
maxschlepzig
fonte
e se não houver espaços, ou seja, o nome do campo estiver entre aspas? por exemplo "campo"
mythz 19/09/10
@mythz: Em seguida, substitua as aspas por novas linhas por tr. Vou atualizar a resposta.
maxschlepzig
1
Esta resposta está incorreta de várias maneiras. É vago: você deve explicar como criar um trcomando que faça o trabalho, em vez de sugerir exemplos que nunca funcionarão em todas as situações. Também corresponderá às palavras que contêm a palavra que você está procurando. A grep -o '\<WORD\>' | wc -lsolução é muito superior.
Sam Hocevar
1
@ Sam, a pergunta deixa isso em aberto, se uma palavra pesquisada deve ser pesquisada como 'WORD' ou '\ <WORD \>' - você pode lê-la dos dois modos. Mesmo que você o leia da 2ª maneira e somente da 2ª maneira, minha resposta estará incorreta apenas de uma maneira. ;) E a solução 'grep -o' é apenas superior, se suportar a opção -o - que não é especificada pelo POSIX ... Bem, acho que o uso de tr é exótico para chamá-lo vaga ...
maxschlepzig
1
@ Kusalananda, bem, ainda é uma ocorrência. Mas se você não quiser contar essas correspondências de substring, leia o último parágrafo da minha resposta e meu comentário anterior aqui.
maxschlepzig 04/04
24

Com o GNU grep, isso funciona: grep -o '\<WORD\>' | wc -l

-o imprime cada parte correspondente de cada linha em uma linha separada.

\<afirma o início de uma palavra e \>o final de uma palavra (semelhante ao Perl \b), para garantir que você não esteja correspondendo a uma string no meio de uma palavra.

Por exemplo,

$ python -c 'importa isso' | grep '\ <um \>'
Deve haver uma - e de preferência apenas uma - maneira óbvia de fazê-lo.
Os namespaces são uma ótima idéia - vamos fazer mais!
$ python -c 'importa isso' | grep -o '\ <one \>'
 um 
um 
um 
$ python -c 'importe isso' | grep -o '\ <um \>' | wc -l
3
efémero
fonte
1
Ou apenasgrep -wo WORD | wc -l
Stéphane Chazelas
10

Infelizmente, isso não funciona com o GNU coreutils.

grep -o -c WORD file

Se funcionar na sua plataforma, é uma solução elegante e bastante intuitiva; mas o pessoal do GNU ainda está pensando.

triplo
fonte
2
Meu mau, o bug está ainda em aberto: savannah.gnu.org/bugs/?33080
tripleee
1
Pena que este teria sido o mais elegante
MasterScrat
Isso funcionou para mim!
ThisaruG
Isto está errado. Isso conta o número de linhas com o padrão WORD. O OP deseja o número total de ocorrências.
Pierre B
@PierreB É por isso que estou dizendo que o GNU greptem um bug aqui. Não está claro no POSIX qual a semântica da combinação -ce -odeve ser, portanto isso atualmente não é portátil. Obrigado pelo comentário; Eu atualizei esta resposta.
Tripleee
7
sed -e 's/[^[:alpha:]]/ /g' text_to_analize.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl 

Este comando faz o seguinte:

  1. Substitua todos os caracteres não alfanuméricos por um espaço em branco.
  2. Todas as quebras de linha também são convertidas em espaços.
  3. Reduz todos os vários espaços em branco para um espaço em branco
  4. Todos os espaços agora são convertidos em quebras de linha. Cada palavra em uma linha.
  5. Traduz todas as palavras para minúsculas, para evitar que 'Hello' e 'olá' sejam palavras diferentes
  6. Classifica o texto
  7. Conta e remove as linhas iguais
  8. Classifica reversa para contar as palavras mais frequentes
  9. Adicione um número de linha a cada palavra para conhecer a palavra posição em todo

Por exemplo, se eu quiser analisar a primeira mensagem de Linus Torvald:

De: [email protected] (Linus Benedict Torvalds) Grupos de notícias: comp.os.minix Assunto: O que você mais gostaria de ver no minix? Resumo: pequena pesquisa para o meu novo sistema operacional ID da mensagem: <[email protected]> Data: 25 de agosto de 91 20:57:08 GMT Organização: Universidade de Helsinque

Olá pessoal, usando o minix -

Estou fazendo um sistema operacional (gratuito) (apenas um hobby, não será grande e profissional como o GNU) para 386 (486) clones AT. Isso está em produção desde abril e está começando a ficar pronto. Gostaria de receber algum feedback sobre coisas que as pessoas gostam / não gostam no minix, pois meu sistema operacional se assemelha um pouco (mesmo layout físico do sistema de arquivos (devido a razões práticas) entre outras coisas).

Atualmente, eu portado bash (1.08) e gcc (1.40), e as coisas parecem funcionar. Isso implica que vou conseguir algo prático dentro de alguns meses e gostaria de saber quais recursos a maioria das pessoas gostaria. Todas as sugestões são bem-vindas, mas não prometo que as implementarei 🙂

Linus ([email protected])

PS. Sim - é livre de qualquer código minix e possui um fs multiencadeado. NÃO é protegível (usa 386 alternância de tarefas etc.) e provavelmente nunca suportará nada além de discos rígidos AT, pois é tudo o que tenho :-(.

Crio um arquivo chamado linus.txt , colo o conteúdo e depois escrevo no console:

sed -e 's/[^[:alpha:]]/ /g' linus.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl 

A saída seria:

 1        7 i
 2        5 to
 3        5 like
 4        5 it
 5        5 and
 6        4 minix
 7        4 a
 8        3 torvalds
 9        3 of
10        3 helsinki
11        3 fi
12        3 any
13        2 would
14        2 won
15        2 what
16        ...

Se você deseja visualizar apenas as 20 primeiras palavras:

sed -e 's/[^[:alpha:]]/ /g' text_to_analize.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl | head -n 20

É importante notar que o comando tr 'AZ' 'a-z' não suport UTF-8 ainda , de modo que em línguas estrangeiras o apres palavra seria traduzido como Apres.

Se você deseja pesquisar apenas a ocorrência de uma palavra, pode adicionar um grep no final:

sed -e 's/[^[:alpha:]]/ /g' text_to_analize.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl | grep "\sword_to_search_for$"

Em um script chamado search_freq :

#!/bin/bash
sed -e 's/[^[:alpha:]]/ /g' text_to_analize.txt | tr '\n' " " |  tr -s " " | tr " " '\n'| tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | nl | grep "\s$1$"

O script deve ser chamado:

 search_freq word_to_search_for
Roger Borrell
fonte
sed: -e expression #2, char 7: unterminated s 'command`, também conta todas as palavras, certo? Mas a OP pediu apenas uma em particular. Também um pouco de explicação seria bom.
Phd
Desculpe, eu cometi um erro. Eu refiz o comando e comentei a resposta. Na minha opinião, da pergunta, é impossível saber se ele gostaria de obter a ocorrência de apenas uma palavra ou uma frequência de ocorrências. Mas caso você queira obter apenas uma palavra, você pode adicionar um grep no final.
Roger Borrell
3

Dependendo se você deseja corresponder a palavra nas chaves ou nos valores dos dados JSON, é provável que você deseje extrair apenas chaves ou apenas valores dos dados. Caso contrário, você poderá contar algumas palavras muitas vezes se elas ocorrerem como chaves e valores.

Para extrair todas as chaves:

jq -r '..|objects|keys[]' <file.json

Isso testa recursivamente se a coisa atual é um objeto e, se for, extrai as chaves. A saída será uma lista de chaves, uma por linha.

Para extrair todos os valores:

jq -r '..|scalars' <file.json

Isso funciona de maneira semelhante, mas possui menos etapas.

Você pode canalizar a saída acima através grep -c 'PATTERN'(para corresponder algum padrão às chaves ou valores), ou grep -c -w -F 'WORD'(para corresponder a uma palavra nas chaves ou valores), ou grep -c -x -F 'WORD'(para corresponder a uma chave ou valor completo), ou similar, a faça a sua contagem.

Kusalananda
fonte
0

Eu tenho json com algo assim: "number":"OK","number":OK"repetido várias vezes em uma linha.

Meu contador "OK" simples:

sed "s|,|\n|g" response | grep -c OK

khazad-dum_miner
fonte
-1

Eu usei o comando awk abaixo para encontrar o número de ocorrências

arquivo de exemplo

file1 gato

praveen ajay 
praveen
ajay monkey praveen
praveen boy praveen

comando:

awk '{print gsub("praveen",$0)}' file1 | awk 'BEGIN{sum=0}{sum=sum+$1}END{print sum}'

resultado

awk '{print gsub("praveen",$0)}' file1 | awk 'BEGIN{sum=0}{sum=sum+$1}END{print sum}'

5
Praveen Kumar BS
fonte
Ou apenas awk '{sum+=gsub("praveen","")} END {print sum+0}'.
G-Man diz 'Reinstate Monica'
Deixe-me saber por que votar para baixo na minha resposta
Praveen Kumar BS