Saya melihat pertanyaan ini dan bertanya-tanya bagaimana saya bisa menerapkan jawaban saya yang menggunakan
sed
menggunakan POSIX
ex
murni
.
Kuncinya adalah bahwa sementara sed
saya bisa membandingkan ruang pegang dengan ruang pola untuk melihat apakah mereka benar-benar setara (dengan
G;/^\(.*\)\n\1$/{do something}
), saya tahu tidak ada cara untuk melakukan tes seperti itu ex
.
Saya tahu bahwa di Vim saya bisa melakukan Y
ank pada baris pertama dan kemudian mengetik
:2,$g/<C-r>0/d
untuk hampir melakukan apa yang saya tentukan — tetapi jika baris pertama berisi apa pun kecuali teks alfanumerik yang sangat mudah, ini memang untung-untungan, karena garis tersebut dibuang sebagai
regex , bukan hanya string untuk perbandingan. (Dan jika baris pertama berisi garis miring, sisa baris akan ditafsirkan sebagai perintah!)
Jadi jika saya ingin menghapus semua baris myfile
yang identik dengan baris pertama — tetapi tidak menghapus baris pertama — bagaimana saya bisa menggunakan itu ex
? Untuk itu, bagaimana saya bisa menggunakannya vi
?
Apakah ada cara POSIX untuk menghapus sebuah baris jika benar-benar cocok dengan baris lain?
Mungkin sesuatu seperti sintaks imajiner ini:
:2,$g/**lines equal to "0**/d
sumber
:execute '2,$g/\V' . escape(getline(1), '\') . '/d'
sed
sebagai filter dari dalamex
, dan menjalankan seluruhsed
jawaban saya di seluruh buffer ... yang akan bekerja, tentu saja (dan sebenarnya portabel tidak sepertised -i
).<C-r>0
sangat baik. Saya tidak yakin Anda bisa melakukan yang lebih baik dengan hanya perintah Ex karena Anda harus melindungi karakter khusus. Tanpa kendala yang sesuai dengan POSIX, saya pikir Anda akan menggunakan saklar yang sangat nomagik\V
dan kemudian Anda akan melindungi backslash (karena tetap mempertahankan makna khususnya\V
) denganescape()
fungsi yang argumen ke-2nya adalah string yang berisi semua karakter yang ingin Anda hindari / lindungi. .:execute '2,$g/\V' . escape(getline(1), '\/') . '/d'
Atau Anda bisa menggunakan karakter lain untuk pembatas pola seperti titik koma. Dalam hal ini, Anda tidak perlu melindungi garis miring ke depan dalam polanya. Ini akan memberikan sesuatu seperti::execute '2,$g;\V' . escape(getline(1), '\') . ';d'
sed
sangat baik. Dengan Vim, Anda sering mendelegasikan tugas khusus tertentu ke program lain, dansed
mungkin merupakan contoh yang baik untuk itu. Omong-omong, Anda tidak harus menjalankansed
seluruh buffer Anda. Jika Anda ingin menjalankannya hanya pada sebagian buffer, Anda bisa memberikan rentang. Misalnya, jika Anda ingin menyaring hanya garis antara 50 dan 100, Anda bisa mengetik::50,100!<your sed command>
.Jawaban:
Vim
Di Vim Anda dapat mencocokkan karakter apa pun termasuk baris baru dengan
\_.
. Anda dapat menggunakan ini untuk membuat pola yang cocok dengan seluruh baris, jumlah barang apa pun, dan kemudian baris yang sama:Sekarang Anda ingin menghapus semua baris dalam file yang cocok dengan yang pertama, tidak termasuk yang pertama. Substitusi untuk menghapus baris terakhir yang cocok dengan yang pertama adalah:
Anda dapat menggunakan
:global
untuk memastikan bahwa substitusi diulangi cukup kali untuk menghapus semua baris:POSIX ex
@saginaw menunjukkan cara yang lebih rapi untuk melakukan ini di Vim dalam komentar untuk pertanyaan Anda, tetapi kami dapat mengadaptasi teknik di atas untuk POSIX ex.
Untuk melakukan ini dengan cara yang kompatibel dengan POSIX, Anda harus melarang pencocokan multi-baris, tetapi Anda masih dapat menggunakan referensi-balik. Ini membutuhkan kerja ekstra:
Berikut rinciannya:
Jadi jika baris saat ini adalah duplikat dari baris 1, daftar x akan berisi
d
. Menjalankannya akan menghapus baris saat ini. Jika itu bukan duplikat, itu akan berisi awalan yang tidak masuk akal"
yang ketika dieksekusi adalah no-op, sejak"
memulai komentar. Saya tidak tahu apakah ini cara paling rapi untuk mencapai ini, itu hanya yang pertama yang terlintas dalam pikiran!Kebetulan baris pertama tidak bisa dihapus karena proses penyalinan sementara mengubah apa baris 1 itu. Jika ini tidak terjadi, Anda bisa mengawali
:g
dengan2,$
rentang sebagai gantinya.Diuji dalam Vim dan ex-vi versi 4.0.
EDIT
Dan cara yang lebih sederhana, yang lolos dari karakter khusus untuk membuat pola pencarian (dengan
'nomagic'
set), membangun sebuah:global
perintah, kemudian menjalankannya:Anda tidak dapat melakukan ini sebagai satu-baris, karena Anda akan memiliki sarang
:global
, yang tidak diizinkan.sumber
Tampaknya satu-satunya cara POSIX untuk melakukan ini adalah dengan menggunakan filter eksternal, seperti
sed
.Misalnya, untuk menghapus baris ke-17 file Anda hanya jika persis sama dengan baris ke-5, dan jika tidak diubah, Anda dapat melakukan hal berikut:
(Anda dapat menjalankan
sed
seluruh buffer di sini, atau Anda dapat menjalankannya hanya pada baris 5-17, tetapi dalam kasus pertama Anda melakukan penyaringan yang tidak perlu — bukan masalah besar — dan dalam kasus terakhir Anda harus menggunakan angka 1 dan 13 dalamsed
perintah Anda, bukan 5 dan 17. Membingungkan.)Karena
sed
hanya melakukan satu forward forward, tidak ada cara mudah untuk melakukan kebalikan dan menghapus baris ke-5 hanya jika identik dengan baris ke-17. Saya mencoba untuk sementara waktu sebagai rasa ingin tahu ... itu sulit .Terobosan - Anda dapat melakukannya seperti ini:
Ini sebenarnya metode yang lebih umum. Itu juga dapat digunakan untuk memberikan hasil yang sama dengan perintah pertama (dan menghapus baris ke-17 hanya jika identik dengan baris ke-5) seperti:
Untuk penggunaan yang lebih luas seperti menghapus semua baris file yang identik dengan baris 37, sambil membiarkan baris 37 tetap utuh, Anda bisa melakukan hal berikut:
Kesimpulannya di sini adalah, untuk memeriksa apakah dua baris identik, alat terbaik adalah
sed
, bukanex
. Tetapi seperti yang disinggung DevSolar dalam komentar , ini bukan kegagalanvi
atauex
—mereka dirancang untuk bekerja dengan alat Unix; itu adalah kekuatan utama.sumber