Mengapa saya harus keluar dari karakter regex di sed untuk diartikan sebagai karakter regex?

11

Sepertinya
cat sed_data.txt | sed 's/\b[0-9]\{3\}\b/NUMBER/g'
saya harus keluar dari karakter untuk membentuk ekspresi reguler. Dalam hal ini saya harus melarikan diri kawat gigi untuk ditafsirkan sebagai beberapa kali.
Mengapa? Saya berharap semuanya akan menjadi karakter regex kecuali lolos. Yaitu sebaliknya.

Jim
sumber
Ada posting tentang pencarian di Vim yang agak mencakup pertanyaan ini, versi singkatnya adalah "itu tergantung pada implementasi perintah" ... unix.stackexchange.com/questions/90345/…
Drav Sloan
@ DravSloan: Saya tidak yakin itu sama. Di Vim Anda mencari teks secara default dan Anda perlu melarikan diri untuk mencari regex. Tapi dalam hal ini formatnya s/regex//gsudah mengharapkan regex dan saya berharap itu adalah teks yang perlu untuk melarikan diri
Jim

Jawaban:

14

Ini karena sedmenggunakan POSIX BREs (Basic Regular Expressions) yang bertentangan dengan EREs (Extended Regular Expressions) yang mungkin Anda gunakan dari Perl atau teman.

Dari sed(1)halaman manual:

REGULAR EXPRESSIONS
       POSIX.2 BREs should be supported, but they aren't completely because of
       performance problems.  The \n sequence in a regular expression  matches
       the newline character, and similarly for \a, \t, and other sequences.

Kutipan yang relevan dari tautan di atas:

Basic Regular Expressions atau BRE flavor membakukan suatu rasa yang mirip dengan yang digunakan oleh perintah grep UNIX tradisional. Ini adalah rasa ekspresi reguler tertua yang masih digunakan sampai sekarang. Satu hal yang membedakan rasa ini adalah bahwa sebagian besar karakter metachar membutuhkan backslash untuk memberikan rasa metacharacter. Kebanyakan rasa lain, termasuk POSIX ERE, menggunakan backslash untuk menekan makna karakter metachar.

Dikutip kata demi kata dari komentar Craig Sanders :

Perhatikan bahwa setidaknya di GNU sed, Anda dapat memberitahu sed untuk menggunakan regexps yang diperluas dengan opsi baris perintah -r atau --regexp-extended. Ini berguna jika Anda ingin menghindari membongkar skrip sed Anda dengan melarikan diri secara berlebihan.

Joseph R.
sumber
1
Perhatikan bahwa setidaknya di GNU sed, Anda dapat memberitahu sed untuk menggunakan regexps yang diperluas dengan opsi -ratau --regexp-extendedbaris perintah. Ini berguna jika Anda ingin menghindari membongkar skrip sed Anda dengan melarikan diri secara berlebihan.
cas
@CraigSanders Terima kasih untuk ini. Ditambahkan ke jawaban.
Joseph R.
@CraigSanders, sedimplementasi lain (ketika mereka mendukung ERE, sebagian besar BSD) cenderung menggunakan -Euntuk itu sebagai gantinya (yang lebih masuk akal karena itu adalah pilihan yang sama seperti untuk grep. Mengapa GNU sedmemilih -radalah misteri bagi saya).
Stéphane Chazelas
ya, sebuah misteri bagiku juga. Akan lebih masuk akal untuk menggunakan -E. dan kemudian tambahkan -F, -G, dan -P agar sesuai dengan GNU grep. IMO gawk juga akan mendapat manfaat dari RE args yang sama ... atau setidaknya, -P.
cas
12

Itu karena alasan historis.

Regexp pertama kali diperkenalkan di Unix di edutilitas pada awal 70-an. Meskipun eddidasarkan pada qedyang pelaksanaannya oleh penulis yang sama dipahami regexp lebih kompleks, edhanya dipahami ^, $, [...], ., *dan \untuk melarikan diri semua hal di atas.

Sekarang, ketika kebutuhan untuk memiliki lebih banyak operator muncul, cara harus ditemukan untuk memperkenalkan mereka tanpa merusak kompatibilitas. Jika skrip digunakan untuk menggunakan s edperintah s/foo() {/foo (var) {/guntuk mengganti semua instance foo() {dengan foo(var) { dan Anda memperkenalkan (atau {operator, itu akan merusak skrip itu.

Namun tidak ada skrip yang akan dilakukan s/foo\(\) {/foo\(var\) {/, karena itu sama dengan s/foo() {/foo(var) {/dan tidak ada alasan untuk melarikan diri (karena itu bukan operator RE. Jadi memperkenalkan operator baru \(atau \{tidak merusak kompatibilitas karena sangat tidak mungkin untuk memecahkan skrip yang ada menggunakan sintaks yang lebih lama.

Jadi, itulah yang dilakukan. Kemudian, \(...\)ditambahkan pada awalnya hanya untuk s edperintah untuk melakukan hal-hal seperti s/foo\(.\)/\1bar/dan kemudian sebagai grep '\(.\)\1'(tetapi bukan hal-hal seperti \(xx\)*).

Dalam UnixV7 (1979, hampir satu dekade kemudian), bentuk baru ekspresi reguler ditambahkan dalam utilitas baru egrepdan awkdisebut extended regular expression (karena mereka adalah alat baru, tidak ada kompatibilitas mundur yang harus dilanggar). Akhirnya, ia menyediakan fungsionalitas yang tersedia di Ken Thompson kuno qed(operator pergantian |, pengelompokan (..)*) dan menambahkan beberapa operator seperti +dan ?(tetapi tidak memiliki fitur backref dari ekspresi reguler dasar).

Kemudian BSD ditambahkan \<dan \>(ke BRE dan ERE), dan SysV ditambahkan \{dan \}ke BRE saja.

Tidak sampai lebih lama dari {dan }ditambahkan ke ERE, dengan melanggar kompatibilitas ke belakang. Tidak semua orang menambahkannya. Misalnya, GNU awkhingga versi 4.0.0 (2011) tidak mendukung {kecuali dipaksa ke mode kesesuaian POSIX.

ketika GNU grepditulis pada awal 90-an, ia menambahkan semua barang dari BSD dan SysV (seperti \<, {) dan alih-alih memiliki dua sintaks dan mesin regexp terpisah untuk BRE dan ERE, mengimplementasikan operator yang sama di keduanya, hanya rekan-rekan BRE dari (, ?, {, +harus didahului dengan backslash (agar kompatibel dengan implementasi lainnya BRE). Itu sebabnya Anda dapat melakukannya .\+di GNU grep(meskipun itu bukan POSIX atau didukung oleh implementasi lain) dan Anda dapat melakukannya (.)\1di GNU egrep(meskipun itu bukan POSIX atau didukung oleh banyak implementasi lain termasuk GNU awk).

Menambahkan \xoperator bukan satu-satunya cara untuk menambahkan lebih banyak operator dengan cara yang kompatibel mundur. Misalnya perldigunakan (?...). Itu masih kompatibel dengan EREs karena (?=...)tidak berlaku di EREs, sama untuk .*?. vimuntuk operator serupa melakukannya secara berbeda dengan memperkenalkan \@=atau .\{-}misalnya.

Stéphane Chazelas
sumber