GNU/Linux >> Linux Esercitazione >  >> Linux

Stampa la riga precedente se la condizione è soddisfatta

Un'altra opzione:invertire il file e stampare il successivo riga se la condizione corrisponde:

tac file | awk '$1 == "BB" && $2 > 1 {getline; print}' | tac

Per quanto riguarda la generalità

Penso che sia necessario menzionare che la soluzione più generale a questa classe di problemi richiede due passaggi:

  • il primo passaggio per aggiungere un numero di riga decimale ($REC) all'inizio di ogni riga, raggruppando effettivamente le righe in record per $REC
  • il secondo passaggio per eseguire il trigger sulla prima istanza di ogni nuovo valore di $REC come limite di record (reimpostando $CURREC), quindi scorrere nell'idioma nativo di AWK riguardante i record da seguire corrispondenti a $CURREC.

Nel file intermedio, una sequenza di cifre decimali seguita da un separatore (per ragioni umane, in genere una tabulazione o uno spazio aggiunto) viene analizzata (ovvero tagliata concettualmente) come fuori banda rispetto al file di base.

Riga di comando incolla mostro

Anche confinato alla riga di comando, è facile assicurarsi che il file intermedio non colpisca mai il disco. Devi solo usare una shell avanzata come ZSH (la mia preferita) che supporta la sostituzione dei processi:

paste <( <input.txt awk "BEGIN { R=0; N=0; } /Header pattern/ { N=1; } { R=R+N; N=0; print R; }" ) input.txt | awk -f yourscript.awk 

Rendiamo quella riga più adatta all'esposizione:

P="/Header pattern/"
X="BEGIN { R=0; N=0; } $P { N=1; } { R=R+N; N=0; print R; }"
paste <( <input.txt awk $X ) input.txt | awk -f yourscript.awk 

Questo avvia tre processi:il banale script AWK in linea, paste e lo script AWK che volevi davvero eseguire in primo luogo.

Dietro le quinte, il <() il costrutto della riga di comando crea una pipe con nome e passa il nome della pipe da incollare come nome del suo primo file di input. Per paste del secondo file di input, gli diamo il nome del nostro file di input originale (questo file viene quindi letto in sequenza, in parallelo, da due processi diversi, che ne consumeranno al massimo uno tra loro letto dal disco, se il file di input è freddo).

La magica pipe nel mezzo è un FIFO in memoria che l'antico Unix probabilmente gestiva a circa 16 kB di dimensione media (mettendo in pausa a intermittenza il paste processo se yourscript.awk il processo è lento nel drenare nuovamente questo FIFO).

Forse il moderno Unix mette un buffer più grande perché può, ma non è certamente una risorsa scarsa di cui dovresti preoccuparti, finché non scrivi il tuo primo veramente riga di comando avanzata con reindirizzamento dei processi che li coinvolge a centinaia o migliaia :-)

Considerazioni aggiuntive sulle prestazioni

Sulle CPU moderne, tutti e tre questi processi potrebbero facilmente trovarsi in esecuzione su core separati.

I primi due di questi processi rasentano il veramente banale:uno script AWK con una singola corrispondenza di pattern e qualche piccola contabilità, incolla chiamata con due argomenti. yourscript.awk sarà difficile correre più veloce di questi.

Cosa, la tua macchina di sviluppo non ha core leggermente caricati per rendere questo modello di soluzione master shell-master quasi libero nel dominio di esecuzione?

Squilla squilla.

Ciao?

Ehi, è per te. Il 2018 è appena arrivato e rivuole il suo problema.

Il 2020 è ufficialmente la tregua di MTV:è così che ci piace, pipe magiche per niente e core gratis. Per non nominare ad alta voce un particolare venditore di chip TLA che sta scuotendo lo spazio in questi giorni.

Come considerazione finale sulle prestazioni, se non vuoi il sovraccarico dell'analisi dei numeri di record effettivi:

X="BEGIN { N=0; } $P { N=1; } { print N; N=0; }"

Ora il tuo file intermedio in-FIFO è annotato con solo altri due caratteri anteposti a ciascuna riga ('0' o '1' e il carattere separatore predefinito aggiunto da paste ), con '1' che segna la prima riga del record.

FIFO denominati

Sotto il cofano, questi non sono diversi dai FIFO magici istanziati da Unix quando scrivi un normale comando pipe:

cat file | proc1 | proc2 | proc2 

Tre pipe senza nome (e un intero processo dedicato a cat non ne avevi nemmeno bisogno).

È quasi un peccato che il veramente eccezionale la comodità dei flussi stdin/stdout predefiniti come pregestiti dalla shell oscura la realtà che paste $magictemppipe1 $magictemppipe2 non comporta ulteriori considerazioni sulle prestazioni a cui vale la pena pensare, nel 99% dei casi.

"Usa il <() Giunto a Y, Luke."

Il tuo riflesso istintivo verso la naturale decomposizione semantica nel dominio del problema ne beneficerà immensamente.

Se qualcuno avesse avuto l'ingegno di nominare il costrutto shell <() come operatore YODA in primo luogo, sospetto che sarebbe stato inserito nel servizio universale almeno un decennio fa.


Questo può essere un modo:

$ awk '$1=="BB" && $2>1 {print f} {f=$1}' file
AAAAAAAAAAAAA

Spiegazione

  • $1=="BB" && $2>1 {print f} se il primo campo è esattamente BB e il secondo campo è più grande di 1 , quindi stampa f , un valore memorizzato.
  • {f=$1} memorizza la riga corrente in f , in modo che sia accessibile durante la lettura della riga successiva.

Linux
  1. Cat Line X to Line Y su un file enorme?

  2. Stampa due file in due colonne?

  3. Stampa la riga precedente dopo una corrispondenza di pattern utilizzando Sed?

  4. Casella occupata Leggi file riga per riga?

  5. Stampa l'ultima riga di un file, dalla CLI

Come leggere i file riga per riga in Bash

Come leggere un file riga per riga in Bash

Comando AWK in Linux/Unix

awk:comando non trovato

comando cut o awk per stampare il primo campo della prima riga

Ansible decommentare la riga nel file