Bagaimana saya bisa menggunakan sed atau ex untuk mengganti blok (kode multi-line) dengan blok teks baru (kode)?

9

Saya harus mengganti blok besar teks (kode skrip shell) dalam file dengan blok teks lain.

Saya terkesan dengan Bagaimana saya bisa menggunakan sed untuk mengganti string multi-line? dijawab oleh antak dan Multi-line replace dijawab oleh Bruce Ediger

Tetapi saya memiliki beberapa kesulitan dalam menggunakannya.

  1. antak sudah disebutkan dalam jawabannya bahwa streaming seluruh file ( 1h;2,$H;$!d;g;) ke buffer tidak disarankan untuk file besar, karena kelebihan memori.

  2. Saya tahu seddapat digunakan dengan fitur blok untuk mempertahankan teks di luar blok tidak berubah. Saya ingin menggunakan fitur ini. Tetapi jika saya menggunakan,

    sed -i '/marker1/,/marker2/s/.*/new text (code)/' filename
    

itu akan menyisipkan teks (kode) baru berulang kali untuk setiap aliran. Karenanya saya harus membuat blok visual sebagai satu aliran, menggunakan sesuatu yang mirip dengan apa yang disarankan oleh antak sebelumnya, tetapi untuk blok (bukan untuk seluruh file).

  1. Seperti yang disebutkan oleh Bruce Ediger, append fitur exyang dimulai dengan adiakhiri dengan .(titik) dapat dicoba, tetapi teks baru saya (kode) berisi baris dimulai dengan titik, yang dapat dianggap sebagai titik sintaks append. Bagaimana saya bisa menggunakannya dalam situasi ini?

  2. ex' ddJumlah baris' dapat menghapus banyak baris, tetapi jika saya memiliki blok antara / marker1 / dan / marker2 / dengan jumlah baris yang tidak diperbaiki (bervariasi) harus diganti dengan teks (kode) baru, bagaimana caranya Itu ?

gibies
sumber
Menurut saya penggunaan istilah "blok visual" tidak biasa dan membingungkan. Apakah yang Anda maksud hanya satu blok (grup) dari garis-garis yang berurutan dan lengkap?
Scott
Ya, saya mengedit pertanyaan untuk menghindari kebingungan.
gibies
Apa sumber untuk teks pengganti? Apakah itu dalam file atau "hanya dalam pikiran pemrogram"?
don_crissti
Ini untuk menambal situasional dari skrip shell. Oleh karena itu sumber untuk teks pengganti adalah pikiran programmer (Atau skrip master di mana sedatau experintah bekerja).
gibies
Don Crissti Saya akan menggunakan rperintah file read, untuk menyalin teks dari file lain, jika sumber untuk teks pengganti ada dalam file.
gibies

Jawaban:

10

Saya sarankan menggunakan c perintah hange (yang pada dasarnya adalah d apus ditambah dengan sebuah ppend, meskipun append tersebut hanya diterapkan untuk baris terakhir dalam kisaran yang tepat apa yang Anda inginkan di sini):

sed -i '/marker1/,/marker2/c\
New text 1\
New text 2' filename

Di sini menggunakan sedsintaksis GNU untuk pengeditan di tempat ( -i). Itu cperintah jika tidak standar dan portabel. GNU sedmendukung:

sed '/marker1/,/marker2/cNew text 1\
New text 2' filename

sebagai ekstensi non-standar.

Karakter baris baru dan garis miring terbalik harus diloloskan (dengan garis miring terbalik) dalam teks pengganti.

G-Man Mengatakan 'Reinstate Monica'
sumber
Perintah perubahan berfungsi dengan baik untuk keduanya seddan exsama - sama. Terima kasih banyak.
gibies
Apakah ada cara untuk menghindari "\" dan menggunakan dokumen di sini untuk sedseperti dalam kasus exsintaks yang disarankan oleh Bruce Ediger di Multi-line replace .
gibies
Anda tidak akan memerlukan garis miring terbalik jika Anda menggunakan rperintah yang dibahas dalam jawaban lain untuk pertanyaan itu. Saya tidak yakin bagaimana Anda akan menggunakan dokumen di sini sed.
G-Man Mengatakan 'Reinstate Monica'
8

Menggunakan GNU sed

Untuk mengganti garis, mulailah pencocokan garis 3dan lanjutkan ke pencocokan garis 5dengan New Code:

$ seq 8 | sed '/3/,/5/{/5/ s/.*/New Code/; t; d}'
1
2
New Code
6
7
8

Untuk garis dalam rentang /3/,/5/, kami menguji untuk melihat apakah garis cocok 5(artinya ini adalah baris terakhir dalam grup) dan, jika demikian, kami melakukan substitusi untuk menyisipkan New Code. Jika substitusi dibuat, maka tperintah itu memberitahu sed untuk melompat ke akhir perintah (dalam hal New Codeini dicetak). Jika tidak, dperintah itu memberitahu sed untuk menghapus baris.

Semua garis lainnya dicetak secara normal.

Untuk mengubah file di tempat, -iopsi dapat digunakan:

sed -i.bak '/3/,/5/{/5/ s/.*/New Code/; t; d}' file

Menggunakan awk

Untuk melakukan hal yang sama menggunakan awk:

$ seq 8 | awk '/3/,/5/{if (!f)print "New Code"; f=1; next}; 1'
1
2
New Code
6
7
8

Perintah awk memperlakukan baris dalam kisaran /3/,/5/secara khusus. Untuk garis dalam rentang itu, kami menguji untuk melihat apakah fnol (yaitu, jika !fbenar) dan, jika demikian, kami mencetak New Codedan mengatur fke 1 dan kemudian kami melewatkan sisa perintah dan melompat ke nextgaris.

Untuk garis di luar rentang /3/,/5/, tidak ada lompatan yang dilakukan dan 1menyebabkan garis yang akan dicetak. Lebih rinci, 1adalah suatu kondisi. Karena 1bukan nol, kondisi dievaluasi ke true. Karena tidak ada tindakan yang dikaitkan dengan kondisi tersebut, tindakan default dilakukan yaitu mencetak garis. Demikianlah 1singkatan untuk print-the-line.

Untuk mengubah file di tempat, -i inplaceopsi dapat digunakan dengan GNU awk4.1 atau lebih tinggi:

awk -i inplace '/3/,/5/{if (!f)print "New Code"; f=1; next} 1' file
John1024
sumber
keren. Saya tidak pandai awk, bisakah Anda menjelaskan sedikit awkekspresi Anda
Rahul
1
@Rahul Tentu. Saya telah menambahkan penjelasan tentang kode awk.
John1024
1
Terima kasih John. Ini persis apa yang saya cari.
gibies
Jauh lebih mudah untuk menghapus blok lalu menambahkan yang baru ke baris terakhir:sed '/3/,/4/d;/5/cNew Code' awk '/5/{print "New Code"}/3/,/5/{next}1'
Costas
1

Berdasarkan pembahasan di atas saya datang dengan solusi menggunakan ex

seq 15 > test1.txt
ex test1.txt << EOEX
/^7/,/^9/c
abcd
123
.xyz
hfr4
.
w!
q
EOEX
cat test1.txt

Di atas memberi

1
2
3
4
5
6
abcd
123
.xyz
hfr4
10
11
12
13
14
15

Terima kasih g-man membuat saya menyadari perintah perubahan dan memberikan klarifikasi tentang titik. Keduanya seddan exberbagi sintaks yang hampir mirip untuk perintah perubahan (juga untuk perintah hapus, tambahkan perintah, dll).

gibies
sumber