Limita il contesto grep a N caratteri on line


31

Devo passare in rassegna alcuni file JSON in cui la lunghezza della linea supera qualche migliaio di caratteri. Come posso limitare grep per visualizzare il contesto fino a N caratteri a sinistra e a destra della partita? Anche qualsiasi strumento diverso da grep andrebbe bene, purché disponibile nei comuni pacchetti Linux.

Questo sarebbe un esempio di output, per il grep switch immaginario Ф :

$ 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



3
Non un duplicato. Si tratta di circa ± caratteri ma l'alternativa suggerita è di circa ± righe. (Il tuo riferimento a StackOverflow è buono, comunque.)
roaima,

Risposte:


22

Con GNU grep:

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

Spiegazione:

  • -o => Stampa solo ciò che hai abbinato
  • -P => Usa espressioni regolari in stile Perl
  • La regex dice che corrisponde a 0 ai $Ncaratteri fooseguito da 0 ai $Ncaratteri.

Se non hai GNU grep:

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

Spiegazione:

Dato che non possiamo più fare affidamento grepsull'essere GNU grep, ci avvaliamo della findricerca ricorsiva di file (l' -razione di GNU grep). Per ogni file trovato, eseguiamo lo snippet Perl.

Interruttori Perl:

  • -n Leggi il file riga per riga
  • -l Rimuovere la nuova riga alla fine di ogni riga e reinserirla durante la stampa
  • -e Considera la seguente stringa come codice

Lo snippet Perl sta essenzialmente facendo la stessa cosa di grep. Inizia impostando una variabile $Nsul numero di caratteri di contesto desiderati. I BEGIN{}mezzi questo viene eseguito solo una volta all'inizio dell'esecuzione non una volta per ogni riga in ogni file.

L'istruzione eseguita per ogni riga è di stampare la riga se la sostituzione regex funziona.

La regex:

  • Abbina pigramente qualsiasi cosa vecchia 1 all'inizio della riga ( ^.*?) seguita da .{0,$N}come nel grepcaso, seguita da fooseguita da un'altra .{0,$N}e infine abbina pigramente qualsiasi cosa vecchia fino alla fine della riga ( .*?$).
  • Lo sostituiamo con $ARGV:$1. $ARGVè una variabile magica che contiene il nome del file corrente in corso di lettura. $1è ciò che corrisponde ai genitori: il contesto in questo caso.
  • Le partite pigre alle due estremità sono necessarie perché una partita golosa mangerebbe tutti i personaggi prima foosenza fallire la corrispondenza (poiché .{0,$N}è consentito abbinare zero volte).

1 Cioè, preferisci non abbinare nulla a meno che ciò non provochi un errore nella corrispondenza generale. In breve, abbina il minor numero possibile di personaggi.


Molto bello grazie. Ciò ha lo svantaggio di evidenziare l'intero output, non solo il testo cercato, ma che può essere aggirato aggiungendo | grep fooalla fine (perdendo comunque l'evidenziazione del nome file nel processo).
dotancohen,

1
@dotancohen Immagino che non puoi vincerli tutti :)
Joseph R.,

Con GNU grepè possibile specificare colori / applicazioni di corrispondenza basati su flag applicati tramite variabili di ambiente. quindi forse anche tu potresti vincerli tutti, (nessuna promessa - nemmeno sicuro che funzionerebbe in questo caso) ma non vedo personalmente la rilevanza qui ... comunque ... continua a giocare.
Mikeserv,

Bella risposta. Solo una nota, usando zshNon sono in grado di farlo funzionare passando N = 10 come nell'esempio. Tuttavia funziona se export N=10prima di eseguire il comando. Qualche idea su come adattare l'esempio per lavorare con zsh?
Gabe Kopley,

Oppureperl -lne 'print "$ARGV: $_" for /.{0,10}foo.{0,10}/g'
Stéphane Chazelas,

20

Prova a usare questo:

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

-E dice che vuoi usare regex esteso

-o dice che vuoi stampare solo la corrispondenza

-r grep sta cercando ricorsivamente risultati nella cartella

REGEX:

{0,10} indica quanti caratteri arbitrari vuoi stampare

. rappresenta un personaggio arbitrario (un personaggio in sé non era importante qui, solo il loro numero)

Modifica: Oh, vedo, che Joseph mi consiglia quasi la stessa soluzione: D


Grazie. Anche se è essenzialmente la stessa soluzione, è fonte di fiducia che questo è il metodo migliore quando due persone lo raccomandano indipendentemente.
dotancohen,

Di
niente

2
Sebbene siano simili, la risposta accettata non ha funzionato per me (ha comunque prodotto lunghe righe), ma una ha funzionato. Il trucco con N = 10 non funziona con una shell bash.
Meesern,

in cygwin -E è significativamente più veloce di -P.
Bob Stein,

2

Tratto da: http://www.topbug.net/blog/2016/08/18/truncate-long-matching-lines-of-grep-a-solution-that-preserves-color/ e https: // stackoverflow. COM / a / 39029954/1150462

L'approccio suggerito ".{0,10}<original pattern>.{0,10}"è perfettamente buono, tranne per il fatto che il colore di evidenziazione è spesso incasinato. Ho creato uno script con un output simile ma anche il colore viene preservato:

#!/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))}"

Supponendo che lo script venga salvato come grepl, quindi grepl pattern file_with_long_linesdovrebbe visualizzare le righe corrispondenti ma con solo 10 caratteri attorno alla stringa corrispondente.


0

Piping stdout to cutcon la -bbandiera; è possibile assegnare l'output di grep a soli byte da 1 a 400 per riga.

grep "foobar" * | cut -b 1-400
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.