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 è esattamenteBB
e il secondo campo è più grande di1
, quindi stampaf
, un valore memorizzato.{f=$1}
memorizza la riga corrente inf
, in modo che sia accessibile durante la lettura della riga successiva.