Apa gunanya menggunakan beberapa tanda seru di sed?

12

Dokumentasi sed POSIX mengatakan:

Suatu fungsi dapat didahului oleh satu atau lebih '!' karakter, dalam hal ini fungsi akan diterapkan jika alamat tidak memilih ruang pola. Nol atau lebih karakter <blank> harus diterima sebelum '!' Yang pertama karakter. Tidak ditentukan apakah karakter <blank> dapat mengikuti '!' karakter, dan aplikasi yang sesuai tidak boleh mengikuti '!' karakter dengan karakter <blank>.

Jadi, dengan sed POSIX, kita dapat:

sed -e '/pattern/!d' file

Itu sama dengan menulis:

sed -e '/pattern/!!d' file

Dan !!!ddan ndari seru tanda masih baik-baik saja (Diuji dengan tiga sedversi dari pusaka toolchest ). Saya tidak melihat adanya manfaat di antara banyak, bukan satu tanda seru.

Mengapa spec mengijinkan sintaks itu dan bagaimana itu berguna dalam aplikasi dunia nyata?


Tampaknya GNU sed tidak sesuai dalam kasus ini, itu akan mengeluh jika kita menggunakan beberapa tanda seru:

$ sed -e '/pattern/!!d' file
sed: -e expression #1, char 11: multiple `!'s
cuonglm
sumber
2
FWIW: Pada OpenBSD !bertindak sebagai toggle, /pattern/!!sama dengan /pattern/, dan /pattern/!!!sama dengan /pattern/!. Pada beberapa FreeBSD !sama dengan satu.
lcd047
2
Inti dari banyak hal dalam spesifikasi adalah sedskrip dapat dibuat . Diberikan POSIX sed, seharusnya menjadi hal yang sangat sederhana untuk menulis naskah penulisan sed. Jadi jika Anda memiliki beberapa pemicu untuk beberapa kasus yang seharusnya menandai alamat yang !tidak layak apa pun tindakan Anda, Anda mungkin bahkan memicu itu beberapa kali untuk yang sama dan masih keluar dengan hasil yang sama.
mikeserv
@cuonglm Tidak, hanya FreeBSD. GNU, OpenBSD, dan NetBSD sedtidak.
lcd047
@ lcd047: ya, tentu saja. Maaf untuk bahasa Inggris saya yang buruk. Maksud saya itu tidak sesuai, kan. Senang mengetahui hal itu. Tetapi poin utama dalam pertanyaan saya adalah bagaimana sintaks itu dapat berguna di dunia nyata, dengan POSIX?
cuonglm
1
FWIW: perbaikan untuk ini telah dilakukan di OpenBSD-current.
lcd047

Jawaban:

5

sedAPI adalah primitif - dan ini adalah desain. Setidaknya, itu tetap primitif dengan desain - apakah itu dirancang secara primitif pada awal saya tidak bisa mengatakan. Dalam kebanyakan kasus, penulisan sedskrip yang, ketika dijalankan, akan menghasilkan skrip lainsed memang masalah sederhana. sedsangat sering diterapkan dengan cara ini oleh preprocessor makro seperti m4dan / atau make.

(Berikut ini adalah kasus penggunaan yang sangat hipotetis: ini adalah masalah yang direkayasa agar sesuai dengan solusi. Jika terasa seperti peregangan bagi Anda, maka itu mungkin karena itu, tetapi itu tidak selalu membuatnya kurang valid.)


Pertimbangkan file input berikut:

cat <<"" >./infile
camel
cat dog camel
dog cat
switch
upper
lower

Jika kami ingin menulis sedskrip yang akan menambahkan kata -case ke ekor setiap kata yang sesuai dalam file input di atas hanya jika dapat ditemukan pada baris dalam konteks yang sesuai , dan kami ingin melakukannya seefisien mungkin ( sebagaimana seharusnya menjadi tujuan kita, misalnya, selama operasi kompilasi) maka kita harus memilih untuk menghindari penerapan /regexp /sebanyak mungkin.

Satu hal yang mungkin kita lakukan adalah mengedit file di sistem kita sekarang, dan tidak pernah menelepon sedsama sekali selama kompilasi. Tetapi jika salah satu dari kata-kata dalam file tersebut harus atau tidak boleh dimasukkan berdasarkan pengaturan lokal dan / atau opsi waktu kompilasi, maka melakukan hal itu kemungkinan tidak akan menjadi alternatif yang diinginkan.

Hal lain yang mungkin kita lakukan adalah memproses file sekarang melawan regexps. Kami dapat memproduksi - dan memasukkan dalam kompilasi kami - sebuah sedskrip yang dapat menerapkan pengeditan sesuai dengan nomor baris - yang biasanya merupakan rute yang jauh lebih efisien dalam jangka panjang.

Sebagai contoh:

n=$(printf '\\\n\t')
grep -En 'camel|upper|lower' <infile |
sed "   1i${n%?}#!/usr/heirloom/bin/posix2001/sed -nf
        s/[^:]*/:&$n&!n;&!b&$n&/;s/://2;\$a${n%?}q"'
        s/ *cat/!/g;s/ *dog/!/g
        s| *\([cul][^ ]*\).*|s/.*/\1-case/p|'

... yang menulis output dalam bentuk sedskrip dan yang terlihat seperti ...

#!/usr/heirloom/bin/posix2001/sed -nf
:1
    1!n;1!b1
    1s/.*/camel-case/p
:2
    2!n;2!b2
    2!!s/.*/camel-case/p
:5
    5!n;5!b5
    5s/.*/upper-case/p
:6
    6!n;6!b6
    6s/.*/lower-case/p
q

Ketika output itu disimpan ke file teks yang dapat dieksekusi pada mesin saya bernama ./bang.seddan jalankan seperti ./bang.sed ./infile, output adalah:

camel-case
upper-case
lower-case

Sekarang Anda mungkin bertanya kepada saya ... Mengapa saya ingin melakukan itu? Mengapa saya tidak hanya grepmencocokkan korek api? Lagi pula, siapa yang menggunakan case unta? Dan untuk setiap pertanyaan yang hanya bisa saya jawab, saya tidak tahu ... karena saya tidak tahu. Sebelum membaca pertanyaan ini, saya tidak pernah secara pribadi memperhatikan multi-! persyaratan parsing dalam spec - saya pikir ini adalah tangkapan yang cukup rapi.

The multi! hal itu segera masuk akal bagi saya, meskipun - banyak sedspesifikasi diarahkan hanya diuraikan dan hanya dihasilkan sed skrip. Anda mungkin akan menemukan \npembatas ewline yang diperlukan untuk [wr:bt{]lebih masuk akal dalam konteks itu, dan jika Anda mengingat gagasan itu, Anda mungkin lebih memahami beberapa aspek spesifikasi lainnya - (seperti :tidak menerima alamat, dan qmenolak untuk terima lebih dari 1) .

Dalam contoh di atas saya menulis formulir tertentu sednaskah yang hanya pernah dibaca sekali. Jika Anda melihatnya dengan saksama, Anda mungkin memperhatikan bahwa ketika sedmembaca file edit, ia berkembang dari satu blok perintah ke blok perintah berikutnya - ia tidak akan pernah bercabang dari atau menyelesaikan skrip-editnya sampai benar-benar selesai dengan file editnya.

Saya menganggap itu multi-! alamat mungkin lebih berguna dalam konteks itu daripada di beberapa orang lain, tetapi, dalam kejujuran, saya tidak bisa memikirkan satu kasus di mana saya mungkin telah menggunakannya dengan sangat baik - dan saya sedbanyak. Saya juga berpikir bahwa GNU / BSD sedgagal untuk menanganinya seperti yang ditentukan - ini mungkin bukan aspek dari spesifikasi yang banyak diminati, jadi jika suatu implementasi mengabaikannya, saya ragu dengan sangat serius bug @ box mereka akan menderita hasilnya sangat buruk.

Yang mengatakan, kegagalan untuk menangani ini sebagaimana ditentukan adalah bug untuk setiap implementasi yang berpura-pura kepatuhan, dan jadi saya pikir menembak email ke kotak dev yang relevan disebut-untuk di sini, dan saya bermaksud melakukannya jika Anda tidak melakukannya.

mikeserv
sumber
1
Sekarang sudah diperbaiki di OpenBSD-current.
lcd047
1
Beberapa !akan dihapus di spec berikutnya , apa yang terjadi di sini!
cuonglm
@cuonglm - terlambat, saya kira. mungkin saya lebih dekat ke sasaran daripada yang saya kira.
mikeserv
@cuonglm - yah, ok, tapi apa artinya ... Diterima sebagai Ditandai ?
mikeserv
1
@ mikeserv: jawabannya menjelaskan keheranan saya dan memberi saya pandangan lain dengan API sed. Masuk akal bagi saya!
cuonglm