grep salta n righe di file e cerca solo dopo


9

Ho un enorme file di registro e voglio grep la prima occorrenza di un modello, e quindi trovare un altro modello subito dopo questa occorrenza.

Per esempio:

123
XXY
214
ABC
182
558
ABC
856
ABC

Nel mio esempio, vorrei trovare 182e quindi trovare la prossima occorrenza diABC

La prima occorrenza è semplice:

grep -n -m1 "182" /var/log/file

Questo produce:

5:182

Come trovo la prossima occorrenza di ABC?

La mia idea era di dire grepdi saltare le prime nrighe (nell'esempio sopra n=5), in base al numero di riga di 182. Ma come posso farlo?


1
È un requisito che grepviene utilizzato? Non credo che questo possa essere fatto con, grepma sarebbe facile con awko sed(da solo o in combinazione con grep).
Hauke ​​Laging,

@HaukeLaging grepnon è richiesto. Non ho ancora familiarità con sedo awk. Se hai una buona soluzione, fammelo sentire! :) @don_crissti dovrebbe essere stampata solo la prima riga. Non mi importa delle altre occorrenze.
Koljanep,

Risposte:


10

Con sedte puoi usare un intervallo e usare l' qinput ad un solo completamento:

sed '/^182$/p;//,/^ABC$/!d;/^ABC$/!d;q'

Allo stesso modo con GNU greppuoi dividere l'input tra due greps:

{ grep -nxF -m1 182; grep -nxF -m1 ABC; } <<\IN
123
XXY
214
ABC
182
558
ABC
856
ABC
IN

... che stampa ...

5:182
2:ABC

... per indicare che il primo ha greptrovato una -Fstringa ixed letterale, l' -xintera riga 182 corrisponde a 5 righe dall'inizio della sua lettura, e la seconda ha trovato una ABC abbinata allo stesso modo 2 righe dall'inizio della sua lettura - o 2 righe dopo il primo grep smettere di leggere alla riga 5.

Da man grep:

-m NUM, --max-count=NUM
          Stop  reading  a  file  after  NUM  matching
          lines.   If the input is standard input from
          a regular file, and NUM matching  lines  are
          output, grep ensures that the standard input
          is  positioned  to  just  after   the   last
          matching  line before exiting, regardless of
          the  presence  of  trailing  context  lines.
          This  enables  a calling process to resume a
          search. 

Ho usato un documento qui per motivi di dimostrazione riproducibile, ma probabilmente dovresti fare:

{ grep ...; grep ...; } </path/to/log.file

Funzionerà anche con altri costrutti di comando composto shell come:

for p in 182 ABC; do grep -nxFm1 "$p"; done </path/to/log.file

+1 L'ho visto nella pagina man. Questo è quello che ho provato, solo con una pipa tra il grep's invece di un ;... no-go
Xen2050

@ Xen2050 - la pipe non funzionerà, di solito - un file cercabile di solito è quello che vuoi quando condividi l'input.
Mikeserv,

Risposta impressionante ma non sostengo la tua affermazione sulle condutture. Il documento qui che i due grepcondividono è effettivamente una pipeline per loro. Qualcos'altro: ho provato senza stampare la linea di marcatura ma sed '//,/^ABC$/!d;/^ABC$/!d;q'genera uno strano errore. Cosa fa //?
Hauke ​​Laging,

1
@HaukeLaging - il documento here (nella maggior parte delle shell) non è una pipe - è un vero file tmp creato dalla shell che la shell elimina prima di scrivere - mantenendo il descrittore. È ancora cercabile. I tubi, in genere, non sono cercabili. Guarderò la sedcosa - l' ho appena scritta molto velocemente.
Mikeserv,

1
@HaukeLaging - Oh, quindi la sedcosa funziona - hai appena lasciato fuori il riferimento. In sedpuoi fare /address/nuovamente riferimento all'ultimo con un //indirizzo vuoto . Quindi lo /^182$/command;//,/next_address/fa e basta /^182$/command;/^182$/,/next_address/. Il tuo errore probabilmente non era una precedente espressione regolare se stavi usando una GNU sed. La cosa di pipe lseek, a proposito, può essere manipolata via indiretta attraverso i /dev/fd/[num]collegamenti sui sistemi Linux - ma se non stai molto attento a gestire bene i buffer (come con dd), di solito è una battaglia persa.
Mikeserv,

2

Utilizzare grepcon le espressioni regolari compatibili con Perl ( pcregrep):

pcregrep -Mo '182(.|\n)*?\KABC'

L'opzione -Mconsente al modello di abbinare più di una linea e \Knon include il modello corrispondente (fino a questo punto) nell'output. Puoi rimuovere \Kse vuoi avere l'intera regione come risultato.


2
> awk '/^182$/ { startline=1; }; startline == 0 { next; }; /^ABC$/ { print "line " NR ": " $0; exit; }' file
line 7: ABC

1
Questo dà la prima ABC ovunque ; questa domanda vuole la prima ABC dopo il primo 182. La più diretta è una bandiera come awk '/^182$/{z=1;next} z&&/^ABC$/{print NR":"$0;exit}' file- o puoi scrivere almeno un getline()ciclo esplicito che di solito è più goffo, o essere intelligente (?) usando un intervallo quasi come il perl di @ JRFerguson:awk '!x&&/^182$/,/^ABC$/ {x=NR":"$0} END{print x}
dave_thompson_085

@ dave_thompson_085 Effettivamente. Idea giusta ma terribilmente codificata (mescolate due idee durante la scrittura). In modo imbarazzante ho anche provato, ma non mi sono meravigliato dell'output.
Hauke ​​Laging,

1

Una variante del Perl che potresti usare è:

perl -nle 'm/182/../ABC/ and print' file

... che stampa le linee nell'intervallo corrispondente.

Se il file conteneva più di un intervallo corrispondente, è possibile limitare l'output al solo primo intervallo modificando il /delimitatore in?

perl -nle 'm?182?..?ABC? and print'

1

Rimanendo con solo grepe aggiungendo tail& cut, potresti ...

grep per il numero di riga della prima corrispondenza di 182:

grep -m 1 -n 182 /var/log/file |cut -f1 -d:

Usalo per grep per tutti ABCsolo dopo la prima riga corrispondente sopra, usando tail's -n +Kper produrre dopo la K'th linea. Tutti insieme:

tail -n +$(grep -m 1 -n 182 /var/log/file |cut -f1 -d:) /var/log/file | grep ABC

Oppure aggiungi di -m 1nuovo per trovare solo la prima corrispondenzaABC

tail -n +$(grep -m 1 -n 182 /var/log/file|cut -f1 -d:) /var/log/file|grep -m 1 ABC

Riferimenti:
manpagine
/programming/6958841/use-grep-to-report-back-only-line-numbers


1

Un'altra variante è questa:

grep -n -A99999 "182" /var/log/file|grep -n -m1 "ABC"

La bandiera -Un greps n righe dopo la partita e 99999 è solo per essere sicuri di non perdere nulla. I file più grandi dovrebbero avere più righe (controlla con "wc -l").


0

L'operatore della gamma ,può essere utilizzato qui:

< yourfile \
sed -e '
   /182/,/ABC/!d
   //!d;=;/ABC/q
' | sed -e 'N;s/\n/:/'

L'operatore di gamma ..in tandem con l' operatore match-only-once m??può essere utilizzato qui inPerl

perl -lne 'm?182? .. m?ABC? and print "$.:$_" if /182/ || /ABC/' yourfile
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.