Quando si utilizza awk / pattern / {print “text”} / patern / {print “”} esiste un pattern ELSE?


22

Diciamo che ho un file di testo come:

R1 12 324 3453 36 457 4 7 8
R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242

Voglio usare awkper elaborare queste linee in modo diverso, come

awk '/R1/ { print "=>" $0} /R2/ { print "*" $0} '

e voglio anche stampare tutto il resto delle linee così come sono (senza creare duplicati delle linee che ho già elaborato), in pratica ho bisogno di un /ELSE/ { print $0}alla fine della mia awklinea.

C'è una cosa del genere?

Risposte:


27

Approccio semplificato con awk

awk '/R1/ {print "=>" $0;next} /R2/{print "*" $0;next} 1' text.file

[jaypal:~/Temp] cat text.file 
R1 12 324 3453 36 457 4 7 8
R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242

[jaypal:~/Temp] awk '/R1/ { print "=>" $0;next} /R2/{print "*" $0;next}1' text.file
=>R1 12 324 3453 36 457 4 7 8
*R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242
[jaypal:~/Temp] 

Breakout of Pattern {Action} Dichiarazioni:

  • /R1/ { print "=>" $0;next}: Ciò significa che verranno eseguite le linee che hanno /R1/l'azione di stampa =>. nextsignifica che il resto delle istruzioni awk verrà ignorato e la riga successiva verrà esaminata.

  • /R2/{print "*" $0;next}: Ciò significa che verranno eseguite le linee corrispondenti pattern /R2/all'azione della stampa *. All'inizio awkdell'elaborazione, la prima pattern {action}istruzione verrà ignorata in quanto pattern /R1/non sarà vera per le righe aventi /R2/. Quindi la seconda pattern {action}affermazione sarà fatta sulla linea. nextsignificherebbe di nuovo che non vogliamo più elaborazioni e awkpasseremo debitamente alla riga successiva.

  • 1stampa tutte le linee. Quando viene fornita solo una condizione senza no {action}, per impostazione predefinita awk utilizza {print}. Qui è la condizione 1che viene interpretata come vera, quindi ha sempre successo. Se arriviamo a questo punto, è perché la prima e la seconda pattern {action}istruzione sono state ignorate o ignorate (per le righe che non contengono /R1/e /R2/), quindi l'azione di stampa predefinita verrà eseguita per le righe rimanenti.


Sembra eseguire marginalmente il più veloce tra tutte le soluzioni pubblicate.
Chris Down,

1
Non sono sicuro che lo zucchero sintattico sia il termine giusto qui ... È solo sintassi.
Daniel Hershcovich,

7

awkimplementa i soliti sospetti quando si tratta di condizionali. È una buona idea utilizzare printfinvece che printper il lavoro che si desidera svolgere in partita.

awk '{ if (/^R1/) { printf("=> %s\n", $0) } else if (/^R2/) { printf("* %s\n", $0) } else { print $0 } }'

Non ne hai davvero bisogno if-then-else.
jaypal singh,

1
Sebbene funzioni perfettamente, non è idiomatico. L'uso giudizioso di nextè uno strumento importante nella programmazione di awk.
dmckee,

2
Non capisco il punto di usare printfqui. Il suo unico vantaggio (a meno che tu non stia eseguendo una formattazione più elaborata della concatenazione) è che non aggiunge una nuova riga, che qui non è rilevante.
Gilles 'SO- smetti di essere malvagio' il

1
È un risultato controintuitivo e sorprendente. Unadorned printdeve solo produrre output $0mentre printfdeve analizzare una stringa di formato.
jw013,

5

Chris Down ha già mostrato come ottenere qualcos'altro per regexps usando un'istruzione esplicita "if" in un blocco. Puoi anche ottenere lo stesso effetto in altri modi, anche se la sua soluzione è probabilmente migliore.

Uno è scrivere una terza regex che corrisponderà solo al testo non abbinato agli altri, nel tuo caso, sarebbe simile a questo:

awk '/^R1/ { print "=>" $0}
     /^R2/ { print "*" $0}
     /^[^R]/ || /^R[^12]/ { print $0 } '

Nota, questo usa regexps ancorati - il ^ all'inizio della regexps corrisponderà solo all'inizio di una riga - i tuoi schemi originali non lo hanno fatto, il che rallenta leggermente la corrispondenza poiché controllerà tutti i caratteri su una riga anziché saltando fino alla riga successiva. Il terzo caso ("else") corrisponderà a una riga che inizia con un carattere che non è 'R' ([^ R]) o che inizia con una 'R' seguita da un carattere che non è un '1' o ' 2 '(R [^ 12]). I due diversi significati di ^ sono alquanto confusi, ma quell'errore è stato fatto molto tempo fa e non verrà cambiato molto presto.

Per usare regexps complementari, hanno davvero bisogno di essere ancorati, altrimenti altrimenti [^ R] corrisponderebbe ad esempio al 1 che lo segue. Per regexps molto semplici come te, questo approccio può essere utile, ma man mano che le regexps diventano più complesse, questo approccio diventerà ingestibile. Invece, puoi usare le variabili di stato per ogni riga, in questo modo:

awk '{ handled = 0 }
     /^R1/ { print "=>" $0; handled = 1}
     /^R2/ { print "*" $0; handled = 1}
     { if (!handled) print $0 } '

Questo imposta a zero per ogni nuova riga, quindi a 1 se corrisponde a una delle due regexps e infine, se è ancora zero, esegue la stampa $ 0.


Va notato che su file di grandi dimensioni entrambi sono meno efficienti dell'uso dei condizionali (come mostrato qui ). rfilesono ripetute solo 10000 righe del set di dati dell'interrogatore.
Chris Down,

4
if (!handled)Che schifo! Utilizzare nextper smettere di considerare altre azioni.
dmckee,

+1 per if (!handled). Le soluzioni generali, flessibili e riutilizzabili sono buone. Cosa succede se la prossima persona che ha questa domanda vuole fare più elaborazioni dopo la stampa? Le risposte con nextnon lo supportano.
Scott,
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.