Limite a saída grep para linhas curtas

8

Costumo usar o grep para encontrar arquivos com uma certa entrada como esta:

grep -R 'MyClassName'

O bom é que ele retorna os arquivos, seu conteúdo e marca a string encontrada em vermelho. O ruim é que eu também tenho arquivos enormes, nos quais todo o texto é escrito em uma única linha grande. Agora, o grep gera muito quando encontra texto nesses arquivos grandes. Existe uma maneira de limitar a saída para, por exemplo, 5 palavras à esquerda e à direita? Ou talvez limite a saída para 30 letras à esquerda e à direita?

Sócrates
fonte
3
Tubos seus resultados atravéscut
Rinzwind
Então, digamos que o padrão que você está procurando esteja na posição 50, mas você disse que quer apenas 30 letras. O que você quer fazer então? Ignore essa linha ou inclua-a na saída, mas apare-a? O que exatamente você deseja limitar - a pesquisa ou as próprias linhas?
Sergiy Kolodyazhnyy
1
@Rinzwind Eu não entendo bem o que você deseja alcançar cut, pois ele só se divide por delimitador ou por contagem de caracteres. Embora quando encontro uma linha com MyClassNameela, ela esteja em qualquer lugar da linha e nem sempre na mesma posição. Além disso, pode haver uma variação de caracteres na frente e atrás, o que quebra a possibilidade de dividir por delimitador.
Sócrates
1
@SergiyKolodyazhnyy Quando for encontrada uma linha positiva MyClassName, quero obter como resultado o nome do arquivo e os caracteres x à esquerda e à direita. x é qualquer número que forneço, por exemplo 30. O restante do conteúdo do arquivo deve ser ignorado. Isso é para obter um contexto para os arquivos correspondentes e limitar a sobrecarga.
Sócrates
1
@Rinzwind Com que tipo de delimitador personalizado você sugeriria cutse houvesse três arquivos com a seguinte entrada: oiadfaosuoianavMyClassNameionaernaldfajde /(/&%%§%/(§(/MyClassName&((/$/$/(§/$&e public class MyClassName { public static void main(String[] args) { } }?
Sócrates

Respostas:

15

grepem si só tem opções de contexto com base em linhas. Uma alternativa é sugerida por este post da SU :

Uma solução alternativa é ativar a opção 'somente correspondência' e usar o poder do RegExp para receber um pouco mais do que o seu texto:

grep -o ".\{0,50\}WHAT_I_M_SEARCHING.\{0,50\}" ./filepath

Obviamente, se você usar o realce de cores, poderá sempre grep novamente para colorir apenas a correspondência real:

grep -o ".\{0,50\}WHAT_I_M_SEARCHING.\{0,50\}"  ./filepath | grep "WHAT_I_M_SEARCHING"

Como outra alternativa, sugiro foldo texto e depois o saudo, por exemplo:

fold -sw 80 input.txt | grep ...

A -sopção fará com que as foldpalavras sejam enviadas para a próxima linha, em vez de se separarem.

Ou use outra maneira de dividir a entrada em linhas com base na estrutura da sua entrada. (A postagem da SU, por exemplo, lidou com JSON, portanto, usar jqetc. para imprimir e grep... ou apenas usar jqpara fazer a filtragem por si só ... seria melhor do que qualquer uma das duas alternativas fornecidas acima.)


Este método GNU awk pode ser mais rápido:

gawk -v n=50 -v RS='MyClassName' '
  FNR > 1 { printf "%s: %s\n",FILENAME, p prt substr($0, 0, n)}
  {p = substr($0, length - n); prt = RT}
' input.txt
  • Diga ao awk para dividir registros no padrão em que estamos interessados ​​( -v RS=...) e no número de caracteres no contexto ( -v n=...)
  • Cada registro após o primeiro registro ( FNR > 1) é aquele em que o awk encontrou uma correspondência para o padrão.
  • Portanto, imprimimos ncaracteres à direita da linha anterior ( p) e ncaracteres iniciais da linha atual ( substr($0, 0, n)), juntamente com o texto correspondente da linha anterior (que é prt)
    • definimos pe prt após a impressão, então o valor que definimos é usado pela próxima linha
    • RT é um GNUism, é por isso que isso é específico do GNU awk.

Para pesquisa recursiva, talvez:

find . -type f -exec gawk -v n=50 -v RS='MyClassName' 'FNR>1{printf "%s: %s\n",FILENAME, p prt substr($0, 0, n)} {p = substr($0, length-n); prt = RT}' {} +
muru
fonte
2
Ok, funciona. Parece que o Regex é uma abordagem válida, então obrigado por isso. O tempo de processamento é bastante grande. Sem Regex, como no meu post acima, leva 4.912s e com Regex, como no seu post, leva 3m39.312s.
Sócrates
1
@Socrates ver se o método awk Eu adicionado acima executa melhor
Muni
1
O foldmétodo pode ser usado apenas se você tiver certeza de que a sequência pesquisada não aparece na borda, caso contrário, ela ficará oculta grep.
Melebius
1
Obrigado por sua sugestão com gawk. Infelizmente, o comando sugerido findproduz resultados aleatórios e sem nomes de arquivos, quando executado no meu sistema. Além disso, não sou fluente o suficiente awkpara analisar corretamente o comando. Atualmente, o Regex em combinação com grepresolve o problema talvez não seja rápido, mas confiável. Mais uma vez, muito obrigado.
Sócrates
1
@Socrates Acho que consegui corrigir o comando awk. Meu modelo mental estava errado sobre quais linhas, RTprefixos etc. deveriam ser usados.
Muni
1

O uso de correspondência apenas em combinação com outras opções (veja abaixo) pode estar muito próximo do que você está procurando, sem a sobrecarga de processamento da regex mencionada na outra resposta

grep -RnHo 'MyClassName'
  • n saída numérica, mostra o número da linha da correspondência
  • H nome do arquivo, mostre o nome do arquivo no início da linha da partida
  • o apenas corresponde, mostra apenas a sequência matemática, não a linha inteira
Robert Riedl
fonte
Embora seja verdade que o resultado é encontrado muito mais rapidamente, faltam informações. O caminho do arquivo é mostrado, o número da linha é mostrado, mas a saída de texto é apenas minha pesquisa inicial MyClassName. Portanto, o contexto está ausente.
Sócrates
grep -RnHo "MyClassName"e grep -Rno "MyClassName"tem a mesma saída.
Sócrates
@Socrates potência não é o mesmo sem H no mesmo directório
Robert Riedl
O -osinalizador pode ser interessante se o regex tiver alguma parte variável. Para uma sequência fixa, é inútil imprimi-la sempre. É provável que o OP esteja interessado em um contexto próximo.
Melebius 18/04
1
@Ocrates, verdadeiro - o contexto está faltando, mas eu pensei que era esse o ponto? Limitar a saída? Você pode adicionar o contexto novamente adicionando as linhas antes ( -B 1) ou depois ( -A 1). Lamento não poder ajudar mais.
Robert Riedl