Come leggere alcune righe dopo aver trovato del testo?


12

Come posso leggere un certo numero di righe dopo aver trovato del testo?

Per esempio.:

Leggi le prossime 2 righe dopo aver trovato "Unix" su:

Test 1
Test 2
Test 3
Test 4
UNIX
Test 5
Test 6
Test 7
Test 8
Test 9

Il risultato può essere:

Test 5
Test 6

Nota: "Unix" sull'ultimo esempio è un argomento e quindi può essere qualsiasi altro testo.

Ciò che ho:

Sono ancora senza idee, ho solo bisogno di una luce. Pensando a creare un altro script per farlo.

Risposte:


10

Una awksoluzione:

$ awk '$0 == "UNIX" {i=1;next};i && i++ <= 2' file
Test 5
Test 6

Spiegazione

  • /^UNIX$/{i=1;next}: se vediamo UNIX, impostiamo la variabile i = 1, elaborando il prossimo input.

  • Se la variabile iè impostata (nel senso che abbiamo visto UNIX), i && i++ <= 2valutata solo al valore vero nelle successive due righe successive UNIX, causando awkun'azione predefinita eseguita print $0.

  • Prima di vedere UNIX, inon è stato definito e inizia alla terza riga dopo UNIX, iaveva un valore maggiore di 2, che rende l'espressione i && i++ <= 2valutata come falsa, senza causare awknulla.


Dopo aver testato la soluzione, sto ricevendo questo messaggio di errore: errore di sistema vicino alla linea 1 che esegue il salvataggio vicino alla linea 1
Freddo,

@Cold: che cosa hai eseguito? Si noti che il $segno all'inizio della mia risposta è il prompt della shell, non parte del awkcomando.
cuonglm,

Un'altra variante:awk '/^UNIX$/ {s=NR;next} s && NR<=s+2'
musiphil,

So che @cuonglm
Cold,

@Cold: qual è il tuo sistema operativo?
cuonglm,

12

Una grepsoluzione:

grep -A2 -P '^UNIX$' file

Spiegazione: -A significa: stampare le due righe successive dopo la corrispondenza

O awk:

awk '$0=="UNIX"{getline; print; getline; print}' file

Spiegazione: Quell'istruzione cerca UNIX nella riga ( $0=="UNIX"). Se viene fornito, ottiene il successivo successivo nel suo buffer ( getline) e stampa il buffer ( print). Questo viene fatto due volte.

Oppure usa sed:

sed -n '/^UNIX$/{n;p;n;p}' file

Spiegazione: Quella ricerca per UNIX ( /^UNIX$/). Se questo viene trovato, esegue la parte in {...}. nsignifica successivo, psignifica stampa. Questo viene fatto anche due volte.


Grazie @chaos, cercherò le ultime 2 opzioni che tu dai. Per favore, aggiungi qualche spiegazione di ogni opzione, lo comprenderò e lo farò.
Freddo,

Se il numero di righe cambia, quante modifiche apporterò sulle ultime due opzioni? Grazie
Freddo

@Cold guarda la mia modifica. Per modificare il numero se le righe ripetono la getline; print;parte awknell'istruzione o la n;p;parte sednell'istruzione.
caos,

Grazie @chaos, ma maggiore è il numero di righe che aumenta l'espressione e il cambiamento non è fattibile secondo me. Non pensi? Se 100 linee?
Freddo,

@Cold Quindi userei la soluzione grep con grep -A100 -P '^UNIX$' file | tail -n +2. La parte di coda è di rimuovere il primo vincolo. Negli altri (sed, awk) dovresti scrivere dei loop, cosa che lo rende meno semplice.
caos,

4
grep -A 2 UNIX file.txt

La manpage di grep descrive così l'opzione:

  -A NUM, --after-context=NUM
      Print NUM  lines  of  trailing  context  after  matching  lines.
      Places  a  line  containing  --  between  contiguous  groups  of
      matches.

Ciao @Twinkles, buona risposta, ma il mio grep ha solo questa opzione "hblcnsviw". Ma la logica è buona. grazie
Freddo

Anche questo verrà stampato UNIXin output.
cuonglm

Omettere il UNIX, pipe a tail: [...] | tail -n +1, o sed: [...] | sed '1d'.
DopeGhoti,

1
@DopeGhoti: la vostra taile sed '1d'suggerimenti funziona correttamente solo se UNIXcompare una sola volta nel testo di input. Tutte le altre risposte consentono più occorrenze. Potrebbe essere meglio suggerire ... | grep -v UNIX. Certo, questo diventa disordinato se UNIXappare nelle righe 15 e 17.
G-Man dice 'Reinstate Monica'

Punti buoni. Sono abbastanza sicuro che potrebbe essere fatto solo sedcon una qualche forma di sed '/UNIX/d;n;n;p/' /path/to/file, che ho appena scoperto e presentato come risposta.
DopeGhoti,

0

Questo sembra fare bene il trucco:

sed -n '/UNIX/{n;p;n;p}' /path/to/file

Verifica teorica:

$ for i in {1..9}; do echo $i; done | sed -n '/4/{n;p;n;p}'
5
6

1
La subshell attorno al tuo forloop non è necessaria.
In pausa fino a ulteriore avviso.

In effetti non lo è; era un residuo di qualche altra fafferia che ero in mezzo a quel guscio all'inizio della giornata. Parens rimossi.
DopeGhoti,

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.