Saya ingin menggunakan sed
untuk menggantikan apa pun di string antara yang pertama AB
dan pertama terjadinya AC
(inklusif) dengan XXX
.
Sebagai contoh , saya memiliki string ini (string ini adalah untuk tes saja):
ssABteAstACABnnACss
dan saya ingin output seperti ini: ssXXXABnnACss
.
Saya melakukan ini dengan perl
:
$ echo 'ssABteAstACABnnACss' | perl -pe 's/AB.*?AC/XXX/'
ssXXXABnnACss
tapi saya ingin mengimplementasikannya dengan sed
. Berikut ini (menggunakan regex yang kompatibel dengan Perl) tidak berfungsi:
$ echo 'ssABteAstACABnnACss' | sed -re 's/AB.*?AC/XXX/'
ssXXXss
text-processing
sed
regular-expression
بارپابابا
sumber
sumber
Jawaban:
Sed regex cocok dengan pertandingan terlama. Sed tidak memiliki padanan yang tidak serakah.
Jelas yang ingin kami lakukan adalah mencocokkan
AB
,diikuti oleh
AC
,diikuti oleh
AC
Sayangnya,
sed
tidak dapat melakukan # 2 - setidaknya tidak untuk ekspresi reguler multi-karakter. Tentu saja, untuk ekspresi reguler karakter tunggal seperti@
(atau bahkan[123]
), kita dapat melakukan[^@]*
atau[^123]*
. Dan agar kita dapat mengatasi keterbatasan sed dengan mengubah semua kejadianAC
menjadi@
dan kemudian mencariAB
,diikuti oleh
@
,diikuti oleh
@
seperti ini:
Bagian terakhir mengubah contoh
@
kembali tak tertandingiAC
.Tapi, tentu saja, ini adalah pendekatan yang ceroboh, karena input sudah bisa mengandung
@
karakter, jadi, dengan mencocokkannya, kita bisa mendapatkan hasil positif palsu. Namun, karena tidak ada variabel shell yang akan memiliki karakter NUL (\x00
) di dalamnya, NUL kemungkinan merupakan karakter yang baik untuk digunakan dalam work-around di atas daripada@
:Penggunaan NUL membutuhkan GNU sed. (Untuk memastikan bahwa fitur GNU diaktifkan, pengguna tidak boleh mengatur variabel shell POSIXLY_CORRECT.)
Jika Anda menggunakan sed dengan
-z
bendera GNU untuk menangani input yang dipisahkan NUL, seperti output darifind ... -print0
, maka NUL tidak akan berada dalam ruang pola dan NUL adalah pilihan yang baik untuk substitusi di sini.Meskipun NUL tidak bisa dalam variabel bash, dimungkinkan untuk memasukkannya dalam a
printf
perintah. Jika string input Anda dapat berisi karakter apa pun, termasuk NUL, maka lihat jawaban Stéphane Chazelas yang menambahkan metode pelolosan yang pintar.sumber
echo
atauprintf
`\ 000 'baik-baik saja di bash (atau input bisa berasal dari file). Tetapi secara umum, serangkaian teks tentu saja tidak mungkin memiliki NUL.AC
keAC@
dan kembali lagi?Beberapa
sed
implementasi memiliki dukungan untuk itu.ssed
memiliki mode PCRE:AT&T as sed memiliki konjungsi dan negasi ketika menggunakan augmented regexps :
Portable, Anda dapat menggunakan teknik ini: ganti string akhir (di sini
AC
) dengan satu karakter yang tidak muncul di string awal atau akhir (seperti di:
sini) sehingga Anda dapat melakukannyas/AB[^:]*://
, dan jika karakter tersebut dapat muncul di input , gunakan mekanisme melarikan diri yang tidak berbenturan dengan string awal dan akhir.Sebuah contoh:
Dengan GNU
sed
, pendekatannya adalah menggunakan baris baru sebagai karakter pengganti. Karenased
memproses satu baris pada satu waktu, baris baru tidak pernah muncul dalam ruang pola, sehingga orang dapat melakukannya:Itu umumnya tidak bekerja dengan
sed
implementasi lain karena mereka tidak mendukung[^\n]
. Dengan GNUsed
Anda harus memastikan bahwa kompatibilitas POSIX tidak diaktifkan (seperti dengan variabel lingkungan POSIXLY_CORRECT).sumber
Tidak, karena regex tidak memiliki kecocokan yang tidak serakah.
Anda dapat mencocokkan semua teks hingga kemunculan pertama
AC
dengan menggunakan "apa pun yang tidak mengandungAC
" diikuti olehAC
, yang melakukan hal yang sama dengan Perl.*?AC
. Masalahnya adalah, "apa pun yang tidak mengandungAC
" tidak dapat diekspresikan dengan mudah sebagai ekspresi reguler: selalu ada ekspresi reguler yang mengenali negasi dari ekspresi reguler, tetapi regasi negasi menjadi rumit dengan cepat. Dan dalam sed portabel, ini tidak mungkin sama sekali, karena regasi negasi memerlukan pengelompokan pergantian yang hadir dalam ekspresi reguler yang diperluas (misalnya dalam awk) tetapi tidak dalam ekspresi reguler dasar portabel. Beberapa versi sed, seperti GNU sed, memang memiliki ekstensi untuk BRE yang membuatnya mampu mengekspresikan semua ekspresi reguler yang mungkin.Karena kesulitan meniadakan regex, ini tidak bisa digeneralisasi dengan baik. Yang bisa Anda lakukan adalah mengubah garis sementara. Dalam beberapa implementasi sed, Anda dapat menggunakan baris baru sebagai penanda, karena mereka tidak dapat muncul di baris input (dan jika Anda membutuhkan banyak penanda, gunakan baris baru diikuti oleh karakter yang berbeda-beda).
Namun, berhati-hatilah bahwa backslash-newline tidak berfungsi di set karakter dengan beberapa versi sed. Secara khusus, ini tidak berfungsi di GNU sed, yang merupakan implementasi sed pada Linux yang tidak tertanam; di GNU sed Anda bisa menggunakan
\n
:Dalam kasus khusus ini, cukup untuk mengganti yang pertama
AC
dengan baris baru. Pendekatan yang saya sampaikan di atas lebih umum.Pendekatan yang lebih kuat dalam sed adalah untuk menyimpan garis ke ruang penahanan, menghapus semua kecuali bagian "menarik" pertama dari garis, menukar ruang penahanan dan ruang pola atau menambahkan ruang pola ke ruang penahan dan ulangi. Namun, jika Anda mulai melakukan hal-hal yang rumit ini, Anda harus benar-benar berpikir untuk beralih ke awk. Awk juga tidak memiliki kecocokan non-serakah, tetapi Anda dapat membagi string dan menyimpan bagian-bagian ke dalam variabel.
sumber
s/\n//g
menghapus semua baris baru.pencocokan sed - non serakah oleh Christoph Sieghart
sumber
Dalam kasus Anda, Anda bisa meniadakan penutupan char dengan cara ini:
sumber
AB
dan pertamaAC
denganXXX
...," dan memberikanssABteAstACABnnACss
sebagai input contoh . Jawaban ini berfungsi untuk contoh itu , tetapi tidak menjawab pertanyaan secara umum. Sebagai contoh,ssABteCstACABnnACss
seharusnya juga menghasilkan outputaaXXXABnnACss
, tetapi perintah Anda melewati baris ini melalui tidak berubah.Solusinya cukup sederhana.
.*
serakah, tetapi tidak sepenuhnya serakah. Pertimbangkan untuk mencocokkanssABteAstACABnnACss
dengan regexpAB.*AC
. YangAC
mengikuti.*
harus benar-benar memiliki kecocokan. Masalahnya adalah karena.*
serakah, yang berikutnyaAC
akan cocok dengan yang terakhirAC
daripada yang pertama..*
memakan yang pertamaAC
sedangkan literalAC
di regexp cocok dengan yang terakhir di ssABteAstACABnn AC ss. Untuk mencegah hal ini terjadi, gantilah yang pertamaAC
dengan yang konyol untuk membedakannya dari yang kedua dan yang lainnya.Serakah
.*
akan berhenti di kaki-foobar-
dissABteAst-foobar-ABnnACss
karena tidak ada yang lain-foobar-
dari ini-foobar-
, dan regexp-foobar-
HARUS memiliki kecocokan. Masalah sebelumnya adalah bahwa regexpAC
memiliki dua pertandingan, tetapi karena.*
serakah, pertandingan terakhir untukAC
dipilih. Namun, dengan-foobar-
, hanya satu pertandingan yang memungkinkan, dan pertandingan ini membuktikan bahwa.*
itu tidak sepenuhnya serakah. Halte bus untuk.*
terjadi di mana hanya satu pertandingan tersisa untuk sisa regexp berikut.*
.Perhatikan bahwa solusi ini akan gagal jika
AC
muncul sebelum yang pertamaAB
karena yang salahAC
akan diganti-foobar-
. Misalnya, setelahsed
substitusi pertama ,ACssABteAstACABnnACss
menjadi-foobar-ssABteAstACABnnACss
; oleh karena itu, kecocokan tidak dapat ditemukan melawanAB.*-foobar-
. Namun, jika urutannya selalu ... AB ... AC ... AB ... AC ..., maka solusi ini akan berhasil.sumber
Salah satu alternatif adalah mengubah string sehingga Anda menginginkan kecocokan serakah
Gunakan
rev
untuk membalikkan string, membalikkan kriteria pertandingan Anda, gunakansed
dengan cara biasa dan kemudian balikkan hasilnya ....sumber