Bagaimana cara mencetak pola regex yang cocok menggunakan awk?

109

Menggunakan awk, saya perlu menemukan kata dalam file yang cocok dengan pola regex.

Saya hanya ingin mencetak kata yang cocok dengan polanya.

Jadi jika sejalan, saya punya:

xxx yyy zzz

Dan pola:

/yyy/

Saya hanya ingin mendapatkan:

yyy

EDIT: berkat kurumi saya berhasil menulis sesuatu seperti ini:

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

dan inilah yang saya butuhkan :) terima kasih banyak!

marverix
sumber
1
@maxtaldykin Bisakah Anda memindahkan jawaban sendiri dari pertanyaan ke jawaban terpisah?
kenorb
2
Anda tidak perlu melakukannya tmp=match($i, /regexp);if(tmp){}, Anda harus bisa melakukannya if(tmp ~ $i){}karena ~artinya "cocok dengan regexp".
JustinCB

Jawaban:

148

Ini yang paling dasar

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

minta awkuntuk mencari patternmenggunakan //, lalu cetak baris, yang secara default disebut record, dilambangkan dengan $ 0. Setidaknya baca dokumentasi .

Jika Anda hanya ingin mencetak kata yang cocok.

awk '{for(i=1;i<=NF;i++){ if($i=="yyy"){print $i} } }' file
kurumi
sumber
49
Karena printtindakan default: awk '/pattern/' filesudah cukup.
Johnsyweb
18
@Johnsyweb, ya saya tahu fakta ini. Untuk pemula seperti marverix, ini dimaksudkan agar lebih visual.
kurumi
21
Saya tidak meragukan pengetahuan Anda. Namun, informasi tersebut mungkin berguna bagi orang lain yang menemukan jawaban ini.
Johnsyweb
2
NB: @marverix harus sedikit lebih banyak pekerjaan rumah agar for-loop berfungsi jika (a) "yyy" adalah ekspresi reguler dan bukan string lurus dan (b) jika "yyy" itu tidak cocok dengan seluruh bidang di dalamnya rekor.
Johnsyweb
8
Ini tidak akan terjadi $i=="yyy"; itu akan menjadi $i ~ /yyy/ekspresi reguler.
JustinCB
118

Sepertinya Anda mencoba meniru grep -operilaku GNU . Ini akan melakukannya asalkan Anda hanya menginginkan pertandingan pertama di setiap baris:

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

Berikut adalah contoh, menggunakan awkimplementasi 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

Baca tentang match, substr, RSTARTdan RLENGTHdi awkmanual.

Setelah itu Anda mungkin ingin memperpanjang ini untuk menangani beberapa pertandingan di baris yang sama.

Johnsyweb
sumber
NB: Untuk menjawab bagian terakhir itu semua konstruk yang dibutuhkan ada di jawaban kurumi dan saya sendiri.
Johnsyweb
Jawaban yang bagus. Hanya saya ingin penjelasan di sini karena saya malas. Tapi itulah mengapa saya menggunakan AWK!
lukas.pukenis
Bagaimana jika saya ingin melakukan sesuatu dengan hasil pertandingan kecuali mencetaknya? Misalnya, saya ingin menambahkan semua kecocokan ke dalam array.
Evya2005
@ evya2005: Anda cukup mengganti panggilan Ron print dengan tugas yang Anda butuhkan.
Johnsyweb
itu tidak berhasil untuk saya. hanya pekerjaan cetak. dapatkah anda menunjukkan contoh kepada saya?
Evya2005
36

gawk bisa mendapatkan bagian yang cocok dari setiap baris menggunakan ini sebagai tindakan:

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

match (string, regexp [, array]) Jika array ada, itu dihapus, dan kemudian elemen ke nol dari array diatur ke seluruh bagian string yang cocok dengan regexp. Jika ekspresi reguler berisi tanda kurung, elemen larik yang diindeks bilangan bulat disetel untuk memuat bagian string yang cocok dengan subekspresi dalam tanda kurung yang sesuai. http://www.gnu.org/software/gawk/manual/gawk.html#String-Functions

royas
sumber
13

Jika Anda hanya tertarik pada baris masukan terakhir dan Anda berharap hanya menemukan satu kecocokan (misalnya bagian dari baris ringkasan dari perintah shell), Anda juga dapat mencoba kode yang sangat ringkas ini, yang diadopsi dari Cara mencetak kecocokan regexp menggunakan `awk`? :

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

Atau versi yang lebih kompleks dengan hasil parsial:

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

Peringatan: awk match()fungsi dengan tiga argumen hanya ada di gawk, bukan dimawk

Berikut adalah solusi bagus lainnya menggunakan ekspresi reguler di belakang grepsebagai ganti awk. Solusi ini memiliki persyaratan yang lebih rendah untuk instalasi Anda:

$ echo "xxx=a yyy=b zzz=c" | grep -Po '(?<=yyy=)[^ ]+'
b
Daniel Alder
sumber
Mengapa Anda menambahkan "tail -n1"? Ini seharusnya bekerja dengan baik tanpanya, bukan?
Arthur Accioly
1
@ArurNyesek. Saya menggunakan istilah tersebut untuk mengekstrak waktu pulang-pergi rata-rata dari panggilan ping, dari situlah asalnya. lucu bahwa butuh 4 tahun untuk menemukannya;)
Daniel Alder
12

Jika Perl adalah sebuah opsi, Anda dapat mencoba ini:

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

Untuk menerapkan pencocokan tidak peka huruf besar / kecil, tambahkan ipengubah

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

Untuk mencetak semuanya SETELAH pertandingan:

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

Untuk mencetak pertandingan dan segalanya setelah pertandingan:

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

Menggunakan sed juga bisa menjadi elegan dalam situasi ini. Contoh (ganti baris dengan grup yang cocok "yyy" dari baris):

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

Halaman manual yang relevan: https://www.gnu.org/software/sed/manual/sed.html#Back_002dreferences-and-Subexpressions

Konrad Brodzik
sumber
Untuk non-gnu sed, solusinya adalah seperti ini:sed -n 's/^.*\(yyy\).*$/\1/gp' < testfile
Grigory Entin
1
@GrigoryEntin - bsd sed berfungsi dengan baik dengan jawaban asli. Perpanjangan regex switch yang didukung oleh POSIX adalah -E, tetapi di FreeBSD setidaknya -r sama dengan -E (-r ditambahkan pada 2010). Bagaimanapun, coba dengan -E (gnu sed menambahkan -E di 4.3)
Juan
3

Di luar topik, ini juga dapat dilakukan dengan menggunakan grep, cukup posting di sini jika ada yang mencari solusi grep

echo 'xxx yyy zzze ' | grep -oE 'yyy'
Zeus
sumber
Cara sederhana untuk mengambilnya bahkan dengan regex. Persis yang saya butuhkan. Terima kasih!
Marquee
Ini berhasil untuk saya; Kasus saya seperti: echo "web_port = 8080, shutdown_port = 8005" | grep -oE "web_port = [0-9] +" # return 8080
Robb Tsang
0

Jika Anda tahu di kolom mana teks / pola yang Anda cari (misalnya "yyy"), Anda dapat memeriksa kolom tertentu untuk melihat apakah cocok, dan mencetaknya.

Misalnya, diberikan file dengan konten berikut, (disebut asdf.txt )

xxx yyy zzz

untuk hanya mencetak kolom kedua jika cocok dengan pola "yyy", Anda dapat melakukan sesuatu seperti ini:

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

Perhatikan bahwa ini juga akan cocok pada dasarnya semua baris yang kolom kedua memiliki "yyy" di dalamnya, seperti ini:

xxx yyyz zzz
xxx zyyyz
kimbo
sumber