Regex pergantian / atau operator (foo | bar) di GNU atau BSD Sed
28
Sepertinya saya tidak bisa membuatnya bekerja. Dokumentasi sed GNU mengatakan untuk melarikan diri dari pipa, tetapi itu tidak berhasil, juga tidak menggunakan pipa lurus tanpa melarikan diri. Menambahkan parens tidak ada bedanya.
$ echo 'cat
dog
pear
banana
cat
dog'| sed 's/cat|dog/Bear/g'
cat
dog
pear
banana
cat
dog
$ echo 'cat
dog
pear
banana
cat
dog'| sed 's/cat\|dog/Bear/g'
cat
dog
pear
banana
cat
dog
echo 'cat dog pear banana cat dog'| sed -E -e 's/cat|dog/Bear/g'
dan itu akan bekerja pada sistem BSD itu, dan sed -rdengan GNU.
GNU sedtampaknya memiliki dukungan yang sepenuhnya tidak berdokumen tetapi berfungsi -E, jadi jika Anda memiliki skrip multi-platform yang terbatas pada di atas, itu adalah pilihan terbaik Anda. Karena tidak didokumentasikan, Anda mungkin tidak dapat benar-benar mengandalkannya.
Sebuah komentar mencatat bahwa versi BSD juga mendukung -rsebagai alias tanpa dokumen. OS X masih tidak hari ini dan mesin NetBSD dan OpenBSD yang lebih tua saya juga tidak memiliki akses, tetapi NetBSD 6.1 yang satu tidak. Unites komersial yang bisa saya jangkau secara universal tidak. Jadi dengan semua pertanyaan portabilitas menjadi cukup rumit pada saat ini, tetapi jawaban sederhana adalah beralih keawk jika Anda membutuhkannya, yang menggunakan ERE di mana-mana.
Tiga BSD yang Anda sebutkan semua dukungan -ropsi sebagai sinonim dari -Euntuk kompatibilitas dengan GNU sed. OpenBSD dan OS X sed -Eakan menafsirkan pipa yang lolos sebagai pipa literal, bukan sebagai operator bergantian. Inilah tautan yang berfungsi ke halaman manual NetBSD dan ini satu untuk OpenBSD yang belum berusia sepuluh tahun.
Ini terjadi karena (a|b)merupakan ekspresi reguler yang diperluas, bukan Ekspresi Reguler Dasar. Gunakan -Eopsi untuk menangani ini.
echo 'cat
dog
pear
banana
cat
dog'|sed -E 's/cat|dog/Bear/g'
Dari sedhalaman manual:
-E Interpret regular expressions as extended (modern) regular
expressions rather than basic regular expressions (BRE's).
Perhatikan bahwa -rini adalah flag lain untuk hal yang sama, tetapi -Elebih portabel dan bahkan akan ada dalam versi berikutnya dari spesifikasi POSIX.
Cara portabel untuk melakukan ini - dan cara yang lebih efisien - adalah dengan alamat. Kamu bisa melakukan ini:
printf %s\\n cat dog pear banana cat dog |
sed -e '/cat/!{/dog/!b'-e '};cBear'
Dengan cara ini jika baris tidak mengandung cat string dan tidak berisi string dogsedb ranches keluar dari skrip, autoprint baris saat ini dan menarik yang berikutnya untuk memulai siklus berikutnya. Karena itu tidak melakukan instruksi selanjutnya - yang dalam contoh inic menggantung seluruh baris untuk membaca Bear tetapi bisa melakukan apa saja.
Ini mungkin perlu dicatat juga bahwa pernyataan apapun menyusul !bdalam sedperintah dapat hanya cocok pada baris yang berisi baik stringdog atau cat- sehingga Anda dapat melakukan tes lebih lanjut tanpa bahaya pencocokan garis yang tidak - yang berarti Anda sekarang dapat menerapkan aturan hanya satu atau yang lain juga.
Tapi selanjutnya. Ini output dari perintah di atas:
###OUTPUT###BearBear
pear
banana
BearBear
Anda juga dapat mengimplementasikan tabel pencarian dengan referensi belakang dengan mudah.
printf %s\\n cat dog pear banana cat dog |
sed '1{x;s/^/ cat dog /;x
};G;s/^\(.*\)\n.* \1 .*/Bear/;P;d'
Lebih banyak pekerjaan yang harus disiapkan untuk contoh kasus sederhana ini, tetapi dapat membuatnya lebih fleksibel sed skrip yang dalam jangka panjang.
Pada baris pertama saya xmengubah ruang pegang dan pola ruang kemudian masukkan string <space>cat <space>dog<space> ke ruang pegang sebelum kita xmengubahnya kembali.
Sejak saat itu dan pada setiap baris berikut saya Gtahan ruang ditambahkan ke ruang pola, lalu periksa untuk melihat apakah semua karakter dari awal baris sampai baris baru yang saya tambahkan pada akhirnya cocok dengan string yang dikelilingi oleh spasi setelahnya. Jika demikian, saya mengganti seluruh lot dengan Bear dan jika tidak, tidak ada salahnya dilakukan karena saya Phanya akan memotong hingga baris pertama yang terjadi pertama kali di ruang pola kemudian dhapus semuanya.
###OUTPUT###BearBear
pear
banana
BearBear
Dan ketika saya mengatakan fleksibel, saya sungguh-sungguh. Ini dia ganti kucing dengan BrownBear dan anjing dengan BlackBear :
printf %s\\n cat dog pear banana cat dog |
sed '1{x;s/^/ 1cat Brown 2dog Black /;x
};G;s/^\(.*\)\n.* [0-9]\1 \([^ ]*\) .*/\2Bear/;P;d'###OUTPUT###BrownBearBlackBear
pear
banana
BrownBearBlackBear
Anda tentu saja dapat memperluas banyak hal pada isi tabel pencarian - saya mengambil ide dari email usenet Greg Ubben tentang masalah tersebut ketika, pada tahun 90-an, dia menggambarkan bagaimana dia membuat kalkulator kasar dari satu sed s///pernyataan.
Fiuh, +1. Anda memiliki kegemaran untuk berpikir di luar kotak yang harus saya katakan
iruvar
@ 1_CR - Lihat hasil edit terakhir saya - bukan ide saya - yang bukan untuk mengatakan bahwa saya tidak menghargai itu dan menganggapnya sebagai pujian. Tapi saya suka memberi kredit di tempat yang seharusnya.
mikeserv
1
ini adalah pertanyaan yang cukup lama, tetapi jika seseorang ingin mencoba, ada upaya yang cukup rendah untuk melakukan ini sed dengan file sed. Setiap opsi dapat didaftar pada baris terpisah, dan sed akan mengevaluasi masing-masing. Ini setara dengan logis atau. Misalnya, untuk menghapus baris yang berisi kode tertentu:
Anda bisa mengatakan: sed -E '/^\/\*!(40103|40101|40111).*\/;$/d'
Berikut adalah teknik yang tidak menggunakan opsi khusus implementasi apa pun untuk sed(misalnya -E, -r). Alih-alih menggambarkan pola sebagai satu regex cat|dog, kita cukup menjalankan seddua kali:
echo 'cat
dog
pear
banana
cat
dog'| sed 's/cat/Bear/g'| sed 's/dog/Bear/g'
Benar-benar solusi yang tepat, tetapi layak untuk dibagikan. Ini secara umum digeneralisasikan ke lebih dari dua string pola, meskipun rantai yang sangat panjang sedtidak terlalu bagus.
Saya sering menggunakan sed -i(yang berfungsi sama di semua implementasi) untuk membuat perubahan pada file. Di sini, daftar panjang string pola dapat dimasukkan dengan baik, karena setiap hasil sementara disimpan ke file:
for pattern in cat dog owl;do
sed -i "s/${pattern}/Bear/g" myfile
done
-r
opsi sebagai sinonim dari-E
untuk kompatibilitas dengan GNU sed. OpenBSD dan OS Xsed -E
akan menafsirkan pipa yang lolos sebagai pipa literal, bukan sebagai operator bergantian. Inilah tautan yang berfungsi ke halaman manual NetBSD dan ini satu untuk OpenBSD yang belum berusia sepuluh tahun.-E
: developer.apple.com/library/mac/documentation/Darwin/Reference/…-E
gnu.org/software/sed/manual/sed.html#index-_002dE .Ini terjadi karena
(a|b)
merupakan ekspresi reguler yang diperluas, bukan Ekspresi Reguler Dasar. Gunakan-E
opsi untuk menangani ini.Dari
sed
halaman manual:Perhatikan bahwa
-r
ini adalah flag lain untuk hal yang sama, tetapi-E
lebih portabel dan bahkan akan ada dalam versi berikutnya dari spesifikasi POSIX.sumber
Cara portabel untuk melakukan ini - dan cara yang lebih efisien - adalah dengan alamat. Kamu bisa melakukan ini:
Dengan cara ini jika baris tidak mengandung cat string dan tidak berisi string dog
sed
b
ranches keluar dari skrip, autoprint baris saat ini dan menarik yang berikutnya untuk memulai siklus berikutnya. Karena itu tidak melakukan instruksi selanjutnya - yang dalam contoh inic
menggantung seluruh baris untuk membaca Bear tetapi bisa melakukan apa saja.Ini mungkin perlu dicatat juga bahwa pernyataan apapun menyusul
!b
dalamsed
perintah dapat hanya cocok pada baris yang berisi baik stringdog
ataucat
- sehingga Anda dapat melakukan tes lebih lanjut tanpa bahaya pencocokan garis yang tidak - yang berarti Anda sekarang dapat menerapkan aturan hanya satu atau yang lain juga.Tapi selanjutnya. Ini output dari perintah di atas:
Anda juga dapat mengimplementasikan tabel pencarian dengan referensi belakang dengan mudah.
Lebih banyak pekerjaan yang harus disiapkan untuk contoh kasus sederhana ini, tetapi dapat membuatnya lebih fleksibel
sed
skrip yang dalam jangka panjang.Pada baris pertama saya
x
mengubah ruang pegang dan pola ruang kemudian masukkan string<space>
cat<space>
dog<space>
ke ruang pegang sebelum kitax
mengubahnya kembali.Sejak saat itu dan pada setiap baris berikut saya
G
tahan ruang ditambahkan ke ruang pola, lalu periksa untuk melihat apakah semua karakter dari awal baris sampai baris baru yang saya tambahkan pada akhirnya cocok dengan string yang dikelilingi oleh spasi setelahnya. Jika demikian, saya mengganti seluruh lot dengan Bear dan jika tidak, tidak ada salahnya dilakukan karena sayaP
hanya akan memotong hingga baris pertama yang terjadi pertama kali di ruang pola kemudiand
hapus semuanya.Dan ketika saya mengatakan fleksibel, saya sungguh-sungguh. Ini dia ganti kucing dengan BrownBear dan anjing dengan BlackBear :
Anda tentu saja dapat memperluas banyak hal pada isi tabel pencarian - saya mengambil ide dari email usenet Greg Ubben tentang masalah tersebut ketika, pada tahun 90-an, dia menggambarkan bagaimana dia membuat kalkulator kasar dari satu
sed s///
pernyataan.sumber
ini adalah pertanyaan yang cukup lama, tetapi jika seseorang ingin mencoba, ada upaya yang cukup rendah untuk melakukan ini sed dengan file sed. Setiap opsi dapat didaftar pada baris terpisah, dan sed akan mengevaluasi masing-masing. Ini setara dengan logis atau. Misalnya, untuk menghapus baris yang berisi kode tertentu:
Anda bisa mengatakan:
sed -E '/^\/\*!(40103|40101|40111).*\/;$/d'
atau letakkan ini di file sed Anda:
sumber
Berikut adalah teknik yang tidak menggunakan opsi khusus implementasi apa pun untuk
sed
(misalnya-E
,-r
). Alih-alih menggambarkan pola sebagai satu regexcat|dog
, kita cukup menjalankansed
dua kali:Benar-benar solusi yang tepat, tetapi layak untuk dibagikan. Ini secara umum digeneralisasikan ke lebih dari dua string pola, meskipun rantai yang sangat panjang
sed
tidak terlalu bagus.Saya sering menggunakan
sed -i
(yang berfungsi sama di semua implementasi) untuk membuat perubahan pada file. Di sini, daftar panjang string pola dapat dimasukkan dengan baik, karena setiap hasil sementara disimpan ke file:sumber