Come stampare un pattern regex abbinato usando awk?


109

Utilizzando awk, ho bisogno di trovare una parola in un file che corrisponda a un pattern regex.

Voglio solo stampare la parola che corrisponde al modello.

Quindi, se in linea, ho:

xxx yyy zzz

E modello:

/yyy/

Voglio solo ottenere:

yyy

EDIT: grazie a kurumi sono riuscito a scrivere qualcosa del genere:

awk '{
        for(i=1; i<=NF; i++) {
                tmp=match($i, /[0-9]..?.?[^A-Za-z0-9]/)
                if(tmp) {
                        print $i
                }
        }
}' $1

e questo è quello che mi serviva :) grazie mille!


1
@maxtaldykin Potresti spostare la tua auto-risposta dalla domanda a una risposta separata per favore?
kenorb

2
Non devi farlo tmp=match($i, /regexp);if(tmp){}, dovresti solo essere in grado di farlo if(tmp ~ $i){}perché ~significa "corrisponde alla regexp".
JustinCB

Risposte:


148

Questa è la base

awk '/pattern/{ print $0 }' file

chiedi awkdi cercare patternusing //, quindi stampa la riga, che per impostazione predefinita è chiamata record, indicata con $ 0. Almeno leggi la documentazione .

Se vuoi solo stampare la parola abbinata.

awk '{for(i=1;i<=NF;i++){ if($i=="yyy"){print $i} } }' file

49
Poiché printè l'azione predefinita: awk '/pattern/' filesarà sufficiente.
Johnsyweb

18
@ Johnsyweb, sì, lo so questo fatto. Per un principiante come Marverix, dovrebbe essere più visivo.
kurumi

21
Non metto in dubbio la tua conoscenza. Tuttavia, le informazioni possono essere utili ad altri che trovano questa risposta.
Johnsyweb

2
NB: @marverix dovrà fare un po 'più di compiti per fare in modo che il forloop funzioni se (a) "yyy" è un'espressione regolare e non una stringa semplice e (b) se "yyy" non corrisponde a un intero campo all'interno un record.
Johnsyweb

8
Non lo sarebbe $i=="yyy"; sarebbe $i ~ /yyy/per un'espressione regolare.
JustinCB

118

Sembra che tu stia cercando di emulare il grep -ocomportamento di GNU . Questo lo farà a condizione che tu voglia solo la prima corrispondenza su ogni riga:

awk 'match($0, /regex/) {
    print substr($0, RSTART, RLENGTH)
}
' file

Ecco un esempio, utilizzando l' awkimplementazione di GNU ():

awk 'match($0, /a.t/) {
    print substr($0, RSTART, RLENGTH)
}
' /usr/share/dict/words | head
act
act
act
act
aft
ant
apt
art
art
art

Leggi su match, substr, RSTARTe RLENGTHnel awkmanuale.

Dopodiché potresti estenderlo per gestire più corrispondenze sulla stessa linea.


NB: Per rispondere a quest'ultima parte, tutti i costrutti necessari sono nella risposta di Kurumi e nella mia.
Johnsyweb

Bella risposta. Vorrei solo una spiegazione qui sul posto perché sono pigro. Ma è per questo che sto usando AWK!
lukas.pukenis

E se volessi fare qualcosa con il risultato della corrispondenza eccetto stamparlo? Ad esempio, voglio aggiungere tutte le corrispondenze nell'array.
Evya2005

@ evya2005: Potresti semplicemente sostituire la chiamata Ron print con il compito che ti serve.
Johnsyweb

non funziona per me. solo lavoro di stampa. puoi mostrarmi un esempio?
Evya2005

36

gawk può ottenere la parte corrispondente di ogni riga usando questo come azione:

{ if (match($0,/your regexp/,m)) print m[0] }

match (string, regexp [, array]) Se array è presente, viene cancellato e quindi l'elemento zeroth di array viene impostato sull'intera porzione di stringa corrispondente a regexp. Se regexp contiene parentesi, gli elementi di array indicizzati con numeri interi sono impostati per contenere la porzione di stringa che corrisponde alla corrispondente sottoespressione tra parentesi. http://www.gnu.org/software/gawk/manual/gawk.html#String-Functions


13

Se sei interessato solo all'ultima riga di input e ti aspetti di trovare una sola corrispondenza (ad esempio una parte della riga di riepilogo di un comando di shell), puoi anche provare questo codice molto compatto, adottato da How to print regexp match usando "awk"? :

$ echo "xxx yyy zzz" | awk '{match($0,"yyy",a)}END{print a[0]}'
yyy

Oppure la versione più complessa con un risultato parziale:

$ echo "xxx=a yyy=b zzz=c" | awk '{match($0,"yyy=([^ ]+)",a)}END{print a[1]}'
b

Attenzione: la awk match()funzione con tre argomenti esiste solo in gawk, non inmawk

Ecco un'altra bella soluzione con un espressione regolare lookbehind nel grepposto di awk. Questa soluzione ha requisiti inferiori per l'installazione:

$ echo "xxx=a yyy=b zzz=c" | grep -Po '(?<=yyy=)[^ ]+'
b

Perché hai aggiunto "tail -n1"? Questo dovrebbe funzionare bene senza di esso, no?
Arthur Accioly

1
@ArthurAccioly Correct. Ho usato il termine per estrarre il tempo medio di andata e ritorno da una chiamata ping, ecco da dove proviene. divertente che ci siano voluti 4 anni per scoprirlo;)
Daniel Alder

12

Se Perl è un'opzione, puoi provare questo:

perl -lne 'print $1 if /(regex)/' file

Per implementare la corrispondenza senza distinzione tra maiuscole e minuscole, aggiungi il imodificatore

perl -lne 'print $1 if /(regex)/i' file

Per stampare tutto DOPO la partita:

perl -lne 'if ($found){print} else{if (/regex(.*)/){print $1; $found++}}' textfile

Per stampare la partita e tutto dopo la partita:

perl -lne 'if ($found){print} else{if (/(regex.*)/){print $1; $found++}}' textfile

3

Anche l'uso di sed può essere elegante in questa situazione. Esempio (sostituire la riga con il gruppo corrispondente "yyy" dalla riga):

$ cat testfile
xxx yyy zzz
yyy xxx zzz
$ cat testfile | sed -r 's#^.*(yyy).*$#\1#g'
yyy
yyy

Pagina di manuale pertinente: https://www.gnu.org/software/sed/manual/sed.html#Back_002dreferences-and-Subexpressions


Per i non gnu sed la soluzione è qualcosa del genere:sed -n 's/^.*\(yyy\).*$/\1/gp' < testfile
Grigory Entin

1
@GrigoryEntin - bsd sed funziona bene con la risposta originale. Lo switch regex esteso supportato da POSIX è -E, ma almeno in FreeBSD -r è lo stesso di -E (-r aggiunto nel 2010). Ad ogni modo, prova con -E (gnu sed ha aggiunto -E in 4.3)
Juan

3

Fuori tema, questo può essere fatto usando anche grep, semplicemente pubblicandolo qui nel caso in cui qualcuno stia cercando una soluzione grep

echo 'xxx yyy zzze ' | grep -oE 'yyy'

Modo semplice per prenderlo anche con regex. Esattamente quello di cui avevo bisogno. Grazie!
Marquee

Questo funziona per me; Il mio caso è come: echo "web_port = 8080, shutdown_port = 8005" | grep -oE "web_port = [0-9] +" # return 8080
Robb Tsang

0

Se sai in quale colonna si trova il testo / modello che stai cercando (ad esempio "yyy"), puoi semplicemente controllare quella specifica colonna per vedere se corrisponde e stamparla.

Ad esempio, dato un file con i seguenti contenuti, (chiamato asdf.txt )

xxx yyy zzz

per stampare solo la seconda colonna se corrisponde al pattern "yyy", potresti fare qualcosa del genere:

awk '$2 ~ /yyy/ {print $2}' asdf.txt

Nota che questo corrisponderà praticamente anche a qualsiasi riga in cui la seconda colonna ha un "yyy", come queste:

xxx yyyz zzz
xxx zyyyz
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.