Regex yang benar tidak bekerja di grep

13

Saya punya regex ini:

(?<=prefix).*$

yang mengembalikan karakter apa pun yang mengikuti string "awalan" dan berfungsi dengan baik pada mesin regex online apa pun (mis. https://regex101.com ). Masalahnya adalah ketika saya menggunakan regex itu di bash:

grep '(?<=prefix).*$' <<< prefixSTRING

itu tidak cocok dengan apa pun. Mengapa regex itu tidak berfungsi dengan grep?

mark009
sumber
11
Ini benar-benar menyoroti mengapa regex101 membutuhkan pemilih rasa POSIX seperti yang dilakukan untuk JS, Perl / PHP, dan Python. Saya tidak bisa menghitung berapa kali saya berharap untuk itu.
Jared Smith
Juga, .*$cocok dengan string apa pun hingga end-of-line (atau end-of-string), bukan hanya satu karakter.
ilkkachu

Jawaban:

38

Anda tampaknya telah mendefinisikan regex yang benar, tetapi tidak mengatur flag yang cukup di command-line untuk grepmemahaminya. Karena secara default grepmendukung BRE dan dengan -Eflag itu ERE. Apa yang Anda miliki (lihat-depan) hanya tersedia dalam rasa regre PCRE yang hanya didukung di GNU grepdengan -Pbenderanya.

Dengan asumsi Anda perlu untuk mengekstrak hanya string pencocokan setelah prefixAnda perlu menambahkan bendera ekstra -ountuk membiarkan tahu grepcetak yang hanya bagian yang cocok sebagai

grep -oP '(?<=prefix).*$' <<< prefixSTRING

Ada juga versi grepyang mendukung pustaka PCRE secara default - pcregrepdi mana Anda bisa melakukannya

pcregrep -o '(?<=prefix).*$' <<< prefixSTRING

Penjelasan terperinci tentang berbagai rasa regex dijelaskan dalam jawaban dan alat Giles yang luar biasa ini yang mengimplementasikan masing-masing

Inian
sumber
38

Ekspresi reguler datang dalam berbagai rasa. Yang Anda perlihatkan adalah ekspresi reguler Perl-like (PCRE, "Perl Compatible Regular Expression").

grepmelakukan ekspresi reguler POSIX. Ini adalah ekspresi reguler dasar (BRE) dan ekspresi reguler yang diperluas (ERE, jika grepdigunakan dengan -Eopsi). Lihat manual untuk re_formatatau regexmanual manual serupa apa pun yang dirujuk manual Anda greppada sistem Anda, atau teks standar POSIX yang baru saja saya tautkan.

Jika Anda menggunakan GNU grep, Anda akan dapat menggunakan ekspresi reguler seperti Perl jika Anda menggunakan opsi spesifik grepGNU .grep-P

Perhatikan juga bahwa grepmengembalikan garis secara default, bukan substring dari garis. Sekali lagi, dengan GNU grep(dan beberapa grepimplementasi lainnya ), Anda dapat menggunakan -oopsi untuk mendapatkan hanya bit yang cocok dengan ekspresi yang diberikan dari setiap baris.

Perhatikan bahwa kedua -Pdan -onon-standar ekstensi POSIX spesifikasigrep .

Jika Anda tidak menggunakan GNU grep, maka Anda dapat menggunakan sedsebagai gantinya untuk mendapatkan bit antara string prefixdan akhir baris:

sed -n 's/.*prefix\(.*\)/\1/p' file

Apa yang dilakukan adalah hanya mencetak garis yang sedmengatur untuk menerapkan substitusi yang diberikan. Substitusi akan mengganti seluruh baris yang cocok dengan ekspresi (yang merupakan BRE), dengan potongan yang terjadi setelah string prefix.

Perhatikan bahwa jika ada beberapa contoh prefixpada sebuah baris, sedvariasi akan mengembalikan string setelah yang terakhir , sedangkan grepvariasi GNU akan mengembalikan string setelah yang pertama (yang akan mencakup contoh lain dari prefix).

The sedsolusi akan portabel untuk semua sistem Unix-seperti.

Kusalananda
sumber
6

Seperti jawaban lain telah menyatakan, greptidak menggunakan rasa regex dengan lookbehinds (secara default dengan GNU grep, atau tidak sama sekali dengan versi lain).

Jika Anda menemukan diri Anda tidak dapat menggunakan GNU grepatau pcregrep, Anda dapat menggunakannya perljika Anda memilikinya.

Setara dengan baris perintah perlakan menjadi:

perl -ne 'print if /(?<=prefix).*$/' <<< prefixSTRING

Anda menempatkan regex yang diinginkan di antara garis miring. Saat Anda menggunakan Perl, ini menggunakan rasa regex Perl .

kuantum
sumber
atau print "$&\n" if ...jika mereka ingin menampilkan hanya bagian setelahprefix
ilkkachu