Limite o contexto do grep para N caracteres on-line

24

Eu tenho que percorrer alguns arquivos JSON nos quais os comprimentos de linha excedem alguns milhares de caracteres. Como posso limitar o grep a exibir o contexto até N caracteres à esquerda e à direita da correspondência? Qualquer outra ferramenta além do grep também estaria bem, desde que disponível em pacotes Linux comuns. / p>

Este seria um exemplo de saída, para o interruptor grep imaginário :

$ grep -r foo *
hello.txt: Once upon a time a big foo came out of the woods.

$ grep -Ф 10 -r foo *
hello.txt: ime a big foo came of t
    
por dotancohen 23.10.2014 / 08:30

3 respostas

18

Com o GNU grep :

N=10; grep -roP ".{0,$N}foo.{0,$N}" .

Explicação:

  • -o = > Imprimir apenas o que você combinou
  • -P = > Use expressões regulares no estilo Perl
  • A regex diz a correspondência 0 a $N caracteres seguidos por foo seguidos por 0 a $N caracteres.

Se você não tiver o GNU grep :

find . -type f -exec \
    perl -nle '
        BEGIN{$N=10}
        print if s/^.*?(.{0,$N}foo.{0,$N}).*?$/$ARGV:$1/
    ' {} \;

Explicação:

Como não podemos mais depender de grep sendo GNU grep , usamos find para procurar arquivos recursivamente (a ação -r do GNU grep ). Para cada arquivo encontrado, executamos o snippet Perl.

Perl muda:

  • -n Leia o arquivo linha por linha
  • -l Remova a nova linha no final de cada linha e coloque-a de volta ao imprimir
  • -e Tratar a seguinte string como código

O trecho de Perl está fazendo essencialmente a mesma coisa que grep . Ele começa definindo uma variável $N para o número de caracteres de contexto que você deseja. O BEGIN{} significa que isso é executado apenas uma vez no início da execução, não uma vez para cada linha em cada arquivo.

A declaração executada para cada linha é para imprimir a linha se a substituição de expressão regular funcionar.

O regex:

  • Corresponda qualquer coisa antiga preguiçosamente 1 no início da linha ( ^.*? ) seguido por .{0,$N} como no caso grep , seguido por foo seguido por outro .{0,$N} e finalmente combinar qualquer coisa antiga preguiçosamente até o final da linha ( .*?$ ).
  • Substituímos isso por $ARGV:$1 . $ARGV é uma variável mágica que contém o nome do arquivo atual que está sendo lido. $1 é o que os parênteses corresponderam: o contexto neste caso.
  • As correspondências lazy em cada extremidade são necessárias porque uma correspondência desejada comeria todos os caracteres antes de foo sem deixar de corresponder (pois .{0,$N} tem permissão para corresponder zero vezes).

1 Ou seja, prefere não corresponder a nada, a menos que isso cause falha na correspondência geral. Resumidamente, combine o menor número de caracteres possível.

    
por 23.10.2014 / 08:51
17

Tente usar este:

grep -r -E -o ".{0,10}wantedText.{0,10}" *

-E informa que você deseja usar o regex estendido

-o indica que você deseja imprimir apenas a correspondência

-r o grep está procurando resultado recursivamente na pasta

REGEX:

{0,10} informa quantos caracteres arbitrários você deseja imprimir

. representa um caractere arbitrário (um caractere em si não era importante aqui, apenas seu número)

Editar: Ah, vejo que Joseph recomenda quase a mesma solução que eu: D

    
por 23.10.2014 / 08:58
0

Extraído de: link e link

A abordagem sugerida ".{0,10}<original pattern>.{0,10}" é perfeitamente boa, exceto pelo fato de que a cor do realce é muitas vezes confusa. Eu criei um script com uma saída semelhante, mas a cor também é preservada:

#!/bin/bash

# Usage:
#   grepl PATTERN [FILE]

# how many characters around the searching keyword should be shown?
context_length=10

# What is the length of the control character for the color before and after the matching string?
# This is mostly determined by the environmental variable GREP_COLORS.
control_length_before=$(($(echo a | grep --color=always a | cut -d a -f '1' | wc -c)-1))
control_length_after=$(($(echo a | grep --color=always a | cut -d a -f '2' | wc -c)-1))

grep -E --color=always "$1" $2 | grep --color=none -oE ".{0,$(($control_length_before + $context_length))}$1.{0,$(($control_length_after + $context_length))}"

Supondo que o script seja salvo como grepl , grepl pattern file_with_long_lines deve exibir as linhas correspondentes, mas com apenas 10 caracteres ao redor da string correspondente.

    
por 14.09.2016 / 06:37