AWK: accedi al gruppo acquisito dal modello di linea


229

Se ho un comando awk

pattern { ... }

e il modello usa un gruppo di acquisizione, come posso accedere alla stringa così catturata nel blocco?



A volte (in casi semplici) è possibile regolare il separatore di campo ( FS) e scegliere ciò che si desidera abbinare a $field. Anche la preformattazione dell'input potrebbe essere d'aiuto.
Krzysztof Jabłoński,

1
C'è una risposta migliore sulla domanda duplicata.
Samuel Edwin Ward, l'

2
Samuel Edwin Ward: Anche questa è una bella risposta! Ma richiede anche gawk(poiché utilizza gensub).
rampion

Risposte:


176

È stata una passeggiata nella memoria ...

Ho sostituito awk perl molto tempo fa.

Apparentemente il motore di espressioni regolari AWK non acquisisce i suoi gruppi.

potresti prendere in considerazione l'utilizzo di qualcosa come:

perl -n -e'/test(\d+)/ && print $1'

il flag -n fa sì che il perl passi su ogni riga come fa awk.


3
Apparentemente qualcuno non è d'accordo. Questa pagina web è del 2005: tek-tips.com/faqs.cfm?fid=5674 Conferma che non è possibile riutilizzare i gruppi corrispondenti in awk.
Peter Tillemans,

3
Preferisco 'perl -n -p -e ...' rispetto a awk per quasi tutti i casi d'uso, dal momento che è più flessibile, più potente e ha una sintassi più sana secondo me.
Peter Tillemans,

15
gawk! = awk. Sono strumenti diversi e gawknon sono disponibili per impostazione predefinita nella maggior parte dei luoghi.
Oli,

6
L'OP ha specificamente richiesto una soluzione awk, quindi non credo che questa sia una risposta.
Joppe,

6
@Joppe non puoi dare una soluzione awk se non c'è soluzione. Nella riga 3 spiego che AWK non supporta l'acquisizione di gruppi e ho fornito un'alternativa, che apparentemente l'OP ha apprezzato perché questa risposta è stata accettata. Come potrei rispondere meglio a questa domanda?
Peter Tillemans,

335

Con gawk, è possibile utilizzare la matchfunzione per acquisire gruppi tra parentesi.

gawk 'match($0, pattern, ary) {print ary[1]}' 

esempio:

echo "abcdef" | gawk 'match($0, /b(.*)e/, a) {print a[1]}' 

uscite cd.

Nota l'uso specifico di gawk che implementa la funzione in questione.

Per un'alternativa portatile è possibile ottenere risultati simili con match()e substr.

esempio:

echo "abcdef" | awk 'match($0, /b[^e]*/) {print substr($0, RSTART+1, RLENGTH-1)}'

uscite cd.


4
Sì, le varianti di gxxx hanno un sacco di bontà e potenza GNU aggiuntive.
Peter Tillemans,

Funziona anche con BusyBox Awk.
MrMas

32

Questo è qualcosa di cui ho bisogno in ogni momento, quindi ho creato una funzione bash per questo. Si basa sulla risposta di Glenn Jackman.

Definizione

Aggiungi questo al tuo .bash_profile ecc.

function regex { gawk 'match($0,/'$1'/, ary) {print ary['${2:-'0'}']}'; }

uso

Cattura regex per ogni riga nel file

$ cat filename | regex '.*'

Cattura il primo gruppo di acquisizione regex per ogni riga nel file

$ cat filename | regex '(.*)' 1

2
In che cosa differisce dall'uso grep -o?
bfontaine,

@bfontaine È stato possibile grep -ogenerare gruppi acquisiti?
Olle Härstedt

1
@ OlleHärstedt No, non è stato possibile. Copre il tuo caso d'uso solo quando non hai gruppi di acquisizione. In quel caso diventa brutto con quello incatenato grep -o.
bfontaine,

15

Puoi usare GNU awk:

$ cat hta
RewriteCond %{HTTP_HOST} !^www\.mysite\.net$
RewriteRule (.*) http://www.mysite.net/$1 [R=301,L]

$ gawk 'match($0, /.*(http.*?)\$/, m) { print m[1]; }' < hta
http://www.mysite.net/

12
+1. Inoltre, con qualsiasi imbarazzo:awk 'match($0, /.*(http.*?)\$/) { print substr($0,RSTART,RLENGTH) }'
Ed Morton,


1
Ed Morton: merita una risposta di alto livello, direi. edit: uhm ... che stampa RewriteRule (.*) http://www.mysite.net/$per me, che è più del sottogruppo.
rampione


4

Puoi anche simulare l'acquisizione in vanilla awk, senza estensioni. Non è intuitivo però:

passaggio 1. usa gensub per circondare le partite con alcuni caratteri che non compaiono nella tua stringa. passaggio 2. Usa la divisione contro il personaggio. passaggio 3. Ogni altro elemento dell'array suddiviso è il gruppo di acquisizione.

$ echo 'ab cb ad' | awk '{split (gensub (/ a ./, SUBSEP "&" SUBSEP, "g", $ 0), cap, SUBSEP); tappo di stampa [2] "|" tappo [4]; }'
ab | annuncio

3
Sono quasi certo che gensubsia una gawkfunzione specifica. Cosa ottieni dal tuo awk se digiti awk --version; -?). Buona fortuna a tutti.
shellter

6
Sono assolutamente certo che gensub sia un gawk-ism, anche se BusyBox awk ce l'ha anche. Questa risposta potrebbe anche essere implementata usando gsub, tuttavia:echo 'ab cb ad' | awk '{gsub(/a./,SUBSEP"&"SUBSEP);split($0,cap,SUBSEP);print cap[2]"|"cap[4]}'
dubiousjim

3
gensub () è un'estensione gawk, il manuale di gawk lo dice chiaramente. Altre varianti awk possono anche implementarlo, ma non è ancora POSIX. Prova gawk --posix '{gsub (...)}' e si lamenterà
MestreLion

2
@MestreLion, vuoi dire che si lamenterà gawk --posix '{gensub(...)}'.
dubiousjim,

1
Nonostante tu abbia torto sul fatto che POSIX awk abbia la gensubfunzione, il tuo esempio si applica a uno scenario molto limitato: l'intero modello è raggruppato, non può corrispondere a qualcosa come tutti key=(value)quando voglio estrarre solo le valueparti.
Meow,

2

Ho faticato un po 'a trovare una funzione bash che avvolga la risposta di Peter Tillemans ma ecco cosa mi è venuto in mente:

funzione regex {perl -n -e "/ $ 1 / && printf \"% s \ n \ "," '$ 1'}

Ho trovato che ha funzionato meglio della funzione bash basata su awk di opsb per il seguente argomento di espressione regolare, perché non voglio che "ms" venga stampato.

'([0-9]*)ms$'

Preferisco questa soluzione, poiché puoi vedere le parti del gruppo che delimitano l'acquisizione, omettendole anche. Tuttavia, qualcuno potrebbe spiegare come funziona? Non riesco a far funzionare correttamente questa sintassi perl in BASH, perché non lo capisco molto bene - specialmente i segni di virgolette doppie / singole intorno$1
Demis

Non è qualcosa che ho fatto prima o da allora, ma guardando indietro ciò che sta facendo è concatenare due stringhe, la prima stringa è racchiusa tra virgolette doppie (questa prima stringa contiene virgolette doppie incorporate con escape con barra rovesciata) e la seconda stringa è tra virgolette singole . Quindi il risultato di tale concatenazione viene fornito come argomento a perl -e. Inoltre, devi sapere che il primo $ 1 (quello tra virgolette doppie) viene sostituito con il primo argomento della funzione, mentre il secondo $ 1 (quello tra virgolette singole) rimane intatto. Vedi questo esempio
wytten

Vedo, ora ha un po 'più senso. Quindi, dov'è nel comando perl la definizione di regex match / group capture? Vedo che hai scritto '([0-9]*)ms$'- è fornito come argomento (e la stringa è un altro argomento)? E l'output di perl -eviene inserito nel printfcomando di bash, quindi, per sostituire %s, è giusto? Grazie, spero di usarlo.
Demis,

1
Si passa un'espressione regolare racchiusa tra virgolette singole come unico argomento per la funzione bash regex. Esempio
wytten il
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.