Masukkan baris setelah pertandingan pertama menggunakan sed

245

Untuk beberapa alasan saya sepertinya tidak dapat menemukan jawaban langsung untuk ini dan saya sedang sedikit kesal saat ini. Bagaimana cara saya memasukkan baris teks pilihan setelah baris pertama yang cocok dengan string tertentu menggunakan sedperintah. Saya sudah ...

CLIENTSCRIPT="foo"
CLIENTFILE="bar"

Dan saya ingin memasukkan garis setelah CLIENTSCRIPT=garis yang menghasilkan ...

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"
pengguna2150250
sumber

Jawaban:

379

Coba lakukan ini dengan menggunakan GNU sed:

sed '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' file

jika Anda ingin mengganti di tempat , gunakan

sed -i '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' file

Keluaran

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"

Dokter

  • lihat sed doc dan cari \a(tambahkan)
Gilles Quenot
sumber
Terima kasih! Dan untuk mendapatkannya menulis kembali ke file tanpa mencetak isi file?
user2150250
11
Perhatikan bahwa keduanya menganggap GNU sed. Itu bukan sedsintaks standar dan tidak akan berfungsi dengan sedimplementasi lainnya .
Stephane Chazelas
Saya kesulitan membuat ini bekerja untuk saya karena saya menggunakan pembatas yang berbeda. Setelah RTFM'ing, saya menyadari saya harus memulai regex dengan \, kemudian pembatas.
Christy
10
Bagaimana HANYA berlaku untuk pertandingan pertama? jelas bahwa itu menambahkan teks setelah pertandingan, tetapi bagaimana ia tahu hanya untuk pertandingan pertama?
Mohammed Noureldin
7
Ini menambahkan teks setelah setiap pertandingan, untuk saya.
schoppenhauer
49

Perhatikan standarnya sed sintaks (seperti pada POSIX, yang didukung oleh semua sedimplementasi yang sesuai di sekitar (GNU, OS / X, BSD, Solaris ...)):

sed '/CLIENTSCRIPT=/a\
CLIENTSCRIPT2="hello"' file

Atau di satu baris:

sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' file

(-e xpressions (dan konten -files) digabungkan dengan baris baru untuk membuat sedinterpretasi skrip sed ).

The -ipilihan untuk mengedit di tempat juga merupakan ekstensi GNU, beberapa implementasi lainnya (seperti FreeBSD) dukungan -i ''untuk itu.

Atau, untuk portabilitas, Anda dapat menggunakan perl :

perl -pi -e '$_ .= qq(CLIENTSCRIPT2="hello"\n) if /CLIENTSCRIPT=/' file

Atau Anda bisa menggunakan edatau ex:

printf '%s\n' /CLIENTSCRIPT=/a 'CLIENTSCRIPT2="hello"' . w q | ex -s file
Stephane Chazelas
sumber
2
Saya mungkin salah, tetapi saat ini sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' filelolos dari kutipan di akhir parameter pertama dan melanggar perintah.
Abandoned Cart
1
@AbandonedCart, dalam cangkang Bourne, cshatau rckeluarga, '...'adalah kutipan kuat di mana backslash tidak istimewa. Satu-satunya pengecualian yang saya tahu adalah fishshell.
Stephane Chazelas
1
Terminal pada MacOS (dan selanjutnya skrip dijalankan di terminal) juga merupakan pengecualian. Saya menemukan sintaks alternatif, tetapi terima kasih.
Abandoned Cart
1
@AbandonedCart, itu sesuatu yang lain. Itu macOS sedtidak sesuai dengan POSIX di sini. Itu membuat pernyataan saya tentang itu menjadi portable salah (untuk varian satu baris). Saya akan meminta konfirmasi kepada grup pembuka apakah ini benar-benar ketidaksesuaian atau salah tafsir standar.
Stephane Chazelas
19

Yang kompatibel dengan POSIX menggunakan sperintah:

sed '/CLIENTSCRIPT="foo"/s/.*/&\
CLIENTSCRIPT2="hello"/' file
SLePort
sumber
1
... yang bahkan mendukung memasukkan baris baru. Aku menyukainya!
Stephan Henningsen
1
Lihat juga sed -e '/.../s/$/\' -e 'CLI.../'yang akan menghindari masalah dengan baris yang mengandung urutan byte yang tidak membentuk karakter yang valid.
Stephane Chazelas
14

Perintah sed yang bekerja pada MacOS (setidaknya, OS 10) dan Unix sama (mis. Tidak memerlukan gnu sed seperti Gilles '(saat ini diterima) seseorang melakukannya):

sed -e '/CLIENTSCRIPT="foo"/a\'$'\n''CLIENTSCRIPT2="hello"' file

Ini berfungsi dalam bash dan mungkin juga shells lain yang mengetahui gaya kutipan evaluasi $ '\ n'. Semuanya bisa dalam satu baris dan bekerja di perintah sed / POSIX yang lebih lama. Jika mungkin ada beberapa baris yang cocok dengan CLIENTSCRIPT = "foo" (atau yang setara dengan Anda) dan Anda hanya ingin menambahkan baris tambahan pertama kali, Anda dapat mengolahnya kembali sebagai berikut:

sed -e '/^ *CLIENTSCRIPT="foo"/b ins' -e b -e ':ins' -e 'a\'$'\n''CLIENTSCRIPT2="hello"' -e ': done' -e 'n;b done' file

(ini membuat loop setelah kode penyisipan baris yang hanya menggilir sisa file, tidak pernah kembali ke perintah sed pertama lagi).

Anda mungkin memperhatikan bahwa saya menambahkan '^ *' ke pola yang cocok kalau-kalau baris tersebut muncul dalam komentar, katakanlah, atau indentasi. Ini tidak 100% sempurna tetapi mencakup beberapa situasi lain yang cenderung umum. Sesuaikan sesuai kebutuhan ...

Kedua solusi ini juga menyelesaikan masalah (untuk solusi umum untuk menambahkan baris) bahwa jika baris baru Anda yang dimasukkan berisi garis miring terbalik atau ampersand mereka akan ditafsirkan dengan sed dan kemungkinan tidak akan keluar sama, seperti halnya \n- misalnya.\0akan menjadi baris pertama yang cocok. Sangat berguna jika Anda menambahkan baris yang berasal dari variabel di mana Anda harus melarikan diri semuanya terlebih dahulu menggunakan $ {var //} sebelumnya, atau pernyataan lain dll.

Solusi ini sedikit kurang berantakan dalam skrip (yang mengutip dan \ n tidak mudah dibaca), ketika Anda tidak ingin meletakkan teks pengganti untuk perintah di awal baris jika mengatakan, dalam suatu fungsi dengan garis lekuk. Saya telah mengambil keuntungan bahwa $ '\ n' dievaluasi ke baris baru oleh shell, ini bukan dalam nilai kutip tunggal '\ n' biasa.

Sudah cukup lama saya pikir perl / awk mungkin menang karena lebih mudah dibaca.

Breezer
sumber
1
Terima kasih atas jawabannya! Yang pertama berfungsi seperti pesona pada AIX OS juga.
abhishek
bagaimana Anda melakukan ini tetapi untuk penyisipan multi-line?
tatsu
1
POSIX sed mendukung baris baru literal dalam string pengganti jika Anda "keluar" dengan backslash ( sumber ). Jadi: s/CLIENTSCRIPT="foo"/&\ [Enter] CLIENTSCRIPT2="hello"/ . Garis miring terbalik harus menjadi karakter terakhir di baris (tidak ada spasi setelahnya), bertentangan dengan tampilannya di komentar ini.
TheDudeAbides
1
@tatsu Newlines dalam string pengganti s///, serta yang ada di adan iperintah dapat "melarikan diri," menggunakan metode yang sama yang saya jelaskan dalam komentar saya di atas.
TheDudeAbides
4

Mungkin agak terlambat untuk mengirim jawaban untuk ini, tetapi saya menemukan beberapa solusi di atas agak rumit.

Saya mencoba penggantian string sederhana di sed dan berhasil:

sed 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file

& tanda mencerminkan string yang cocok, lalu Anda menambahkan \ n dan baris baru.

Seperti yang disebutkan, jika Anda ingin melakukannya di tempat:

sed -i 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file

Hal lain. Anda dapat mencocokkan menggunakan ekspresi:

sed -i 's/CLIENTSCRIPT=.*/&\nCLIENTSCRIPT2="hello"/' file

Semoga ini bisa membantu seseorang

siwesam
sumber
Ini berfungsi dengan baik di Linux, di mana GNU sed adalah default, karena ia mengerti \ndi string pengganti. Plain-vanili (POSIX) sedtidak tidak mengerti \ndalam string pengganti, namun, jadi ini tidak akan bekerja pada BSD atau MacOS-kecuali jika Anda telah menginstal GNU sed entah bagaimana. Dengan vanilla sed Anda dapat menyematkan baris baru dengan "melarikan diri" - yaitu, mengakhiri garis dengan garis miring terbalik, kemudian menekan [Enter] ( referensi , di bawah judul untuk [2addr]s/BRE/replacement/flags). Ini berarti perintah sed Anda harus menjangkau beberapa baris di terminal.
TheDudeAbides
Opsi lain untuk mendapatkan baris baru literal dalam string pengganti adalah dengan menggunakan fitur kutipan ANSI-C di Bash, sebagaimana disebutkan dalam salah satu jawaban lain .
TheDudeAbides
4

Varian awk:

awk '1;/CLIENTSCRIPT=/{print "CLIENTSCRIPT2=\"hello\""}' file
Corentin Limier
sumber
1
Untuk siapa pun yang bertanya-tanya tentang 1;, lihat Mengapa "1" di awk mencetak baris saat ini?
cherdt
1

Saya memiliki tugas yang sama, dan tidak bisa mendapatkan solusi perl di atas untuk bekerja.

Ini solusinya:

perl -i -pe "BEGIN{undef $/;} s/^\[mysqld\]$/[mysqld]\n\ncollation-server = utf8_unicode_ci\n/sgm" /etc/mysql/my.cnf

Penjelasan:

Menggunakan ekspresi reguler untuk mencari baris di file /etc/mysql/my.cnf saya yang hanya berisi [mysqld]dan menggantinya dengan

[mysqld] collation-server = utf8_unicode_ci

secara efektif menambahkan collation-server = utf8_unicode_cibaris setelah baris yang mengandung [mysqld].

Caleb
sumber
0

Saya harus melakukan ini baru-baru ini juga untuk Mac dan Linux OS dan setelah browsing melalui banyak posting dan mencoba banyak hal, menurut pendapat khusus saya, saya tidak pernah sampai ke tempat yang saya inginkan yaitu: solusi yang cukup sederhana untuk memahami menggunakan terkenal dan perintah standar dengan pola sederhana, satu liner, portabel, dapat diperluas untuk menambah lebih banyak kendala. Kemudian saya mencoba melihatnya dengan perspektif yang berbeda, saat itulah saya menyadari bahwa saya dapat melakukannya tanpa opsi "satu liner" jika "2-liner" memenuhi seluruh kriteria saya. Pada akhirnya saya datang dengan solusi ini. Saya suka itu bekerja di Ubuntu dan Mac yang ingin saya bagikan dengan semua orang:

insertLine=$(( $(grep -n "foo" sample.txt | cut -f1 -d: | head -1) + 1 ))
sed -i -e "$insertLine"' i\'$'\n''bar'$'\n' sample.txt

Pada perintah pertama, grep mencari nomor baris yang berisi "foo", cut / head memilih kemunculan ke-1, dan kenaikan aritmatika menambahkan nomor baris kemunculan pertama sebanyak 1 karena saya ingin memasukkan setelah kemunculannya. Pada perintah kedua, ini merupakan edit file di tempat, "i" untuk menyisipkan: sebuah ansi-c mengutip baris baru, "bar", lalu baris baru lainnya. Hasilnya menambahkan baris baru yang mengandung "bar" setelah baris "foo". Masing-masing dari 2 perintah ini dapat diperluas ke operasi yang lebih kompleks dan pencocokan.

momonari8
sumber