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!


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

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.


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


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

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

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


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'

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.