Saya ingin memperbarui sejumlah besar file sumber C ++ dengan tambahan termasuk arahan sebelum ada #include. Untuk tugas semacam ini, saya biasanya menggunakan skrip bash kecil dengan sed untuk menulis ulang file.
Bagaimana saya bisa sed
mengganti hanya kemunculan pertama string dalam file daripada mengganti setiap kejadian?
Jika saya gunakan
sed s/#include/#include "newfile.h"\n#include/
ini menggantikan semua #tercakup.
Saran alternatif untuk mencapai hal yang sama juga diterima.
command-line
sed
text-processing
David Dibben
sumber
sumber
0,
hanya bekerja dengangnu sed
s//
- ie, regex kosong - berarti regex yang paling baru diterapkan secara implisit digunakan kembali; dalam hal iniRE
,. Pintasan praktis ini berarti Anda tidak perlu menduplikasi regex akhiran jangkauan dalams
panggilan Anda .Sebuah
sed
skrip yang hanya akan menggantikan kemunculan pertama "Apple" dengan "Banana"Contoh
Ini adalah skrip sederhana: Catatan editor: hanya berfungsi dengan GNU
sed
.Dua parameter pertama
0
dan/Apple/
merupakan penentu rentang. Inilahs/Apple/Banana/
yang dieksekusi dalam rentang itu. Jadi dalam hal ini "dalam rentang awal (0
) hingga instance pertamaApple
, gantiApple
denganBanana
. Hanya yang pertama yangApple
akan diganti.Latar Belakang: Secara tradisional
sed
rentang penentu juga "mulai di sini" dan "akhiri di sini" (inklusif). Namun "awal" yang terendah adalah baris pertama (baris 1), dan jika "akhiri di sini" adalah sebuah regex, maka itu hanya berusaha untuk mencocokkan dengan pada baris berikutnya setelah "mulai", sehingga ujung yang paling awal mungkin adalah garis 2. Jadi karena rentang inklusif, kisaran terkecil yang mungkin adalah "2 baris" dan rentang awal terkecil adalah kedua baris 1 dan 2 (yaitu jika ada kejadian pada baris 1, kejadian pada baris 2 juga akan berubah, tidak diinginkan dalam hal ini ).GNU
sed menambahkan ekstensi sendiri yang memungkinkan menetapkan awal sebagai "semu"line 0
sehingga akhir rentang dapatline 1
, memungkinkan rentang "hanya baris pertama"Atau versi yang disederhanakan (RE seperti kosong
//
berarti menggunakan kembali yang ditentukan sebelumnya, jadi ini setara):Dan kurung kurawal adalah opsional untuk
s
perintah, jadi ini juga setara:Semua ini hanya berfungsi pada GNU
sed
.Anda juga dapat menginstal sed GNU pada OS X menggunakan homebrew
brew install gnu-sed
.sumber
sed: 1: "…": bad flag in substitute command: '}'
sed -e '1s/Apple/Banana/;t' -e '1,/Apple/s//Banana/'
. Dari jawaban @ MikhailVS (saat ini) di bawah.sed '0,/foo/s/foo/bar/'
sed: -e expression #1, char 3: unexpected
, '' dengan iniini berhasil untuk saya.
contoh
Catatan editor: keduanya hanya bekerja dengan GNU
sed
.sumber
sed '1,/pattern/s/pattern/replacement/' filename
hanya berfungsi jika "polanya tidak akan muncul di baris pertama" di Mac. Saya akan menghapus komentar saya sebelumnya karena tidak akurat. Detailnya dapat ditemukan di sini ( linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/… ). Jawaban Andy hanya berfungsi untuk GNU sed, tetapi tidak pada Mac.Gambaran umum dari banyak jawaban yang ada dan bermanfaat , dilengkapi dengan penjelasan :
Contoh di sini menggunakan use case yang disederhanakan: ganti kata 'foo' dengan 'bar' di baris pertama yang cocok saja.
Karena penggunaan string ANSI C-dikutip (
$'...'
) untuk menyediakan jalur input sampel,bash
,ksh
, atauzsh
diasumsikan sebagai shell.Hanya GNU
sed
:Jawaban Ben Hoffstein menunjukkan kepada kita bahwa GNU menyediakan ekstensi untuk spesifikasi POSIX
sed
yang memungkinkan formulir 2-alamat berikut :0,/re/
(re
mewakili ekspresi reguler yang sewenang-wenang di sini).0,/re/
memungkinkan regex untuk mencocokkan pada baris pertama juga . Dengan kata lain: alamat seperti itu akan membuat rentang dari baris pertama hingga dan termasuk baris yang cocokre
- apakahre
terjadi pada baris pertama atau pada baris berikutnya.1,/re/
, yang membuat rentang yang cocok dari baris pertama hingga dan termasuk baris yang cocokre
dengan baris berikutnya ; dengan kata lain: ini tidak akan mendeteksi kemunculan pertama suature
kecocokan jika kebetulan terjadi pada baris pertama dan juga mencegah penggunaan steno//
untuk penggunaan kembali regex yang terakhir digunakan (lihat poin berikutnya). 1Jika Anda menggabungkan
0,/re/
alamat dengan panggilans/.../.../
(substitusi) yang menggunakan ekspresi reguler yang sama , perintah Anda hanya akan melakukan substitusi pada baris pertama yang cocokre
.sed
menyediakan cara pintas yang nyaman untuk menggunakan kembali ekspresi reguler yang paling terakhir diterapkan : pasangan pembatas kosong//
,.Hanya fitur POSIX
sed
seperti BSD (macOS)sed
(juga akan berfungsi dengan GNUsed
):Karena
0,/re/
tidak dapat digunakan dan formulir1,/re/
tidak akan mendeteksire
jika terjadi pada baris pertama (lihat di atas), penanganan khusus untuk baris 1 diperlukan .Jawaban MikhailVS menyebutkan teknik tersebut, dimasukkan ke dalam contoh nyata di sini:
catatan:
//
Pintasan regex kosong digunakan dua kali di sini: sekali untuk titik akhir rentang, dan sekali dalams
panggilan; dalam kedua kasus, regexfoo
secara implisit digunakan kembali, memungkinkan kita tidak harus menduplikatnya, yang membuat kode menjadi lebih pendek dan lebih mudah dikelola.POSIX
sed
membutuhkan baris baru aktual setelah fungsi tertentu, seperti setelah nama label atau bahkan penghilangannya, seperti halnya dit
sini; memisahkan skrip secara strategis menjadi beberapa-e
opsi merupakan alternatif untuk menggunakan baris baru yang sebenarnya: akhiri setiap-e
potongan skrip di mana baris baru biasanya perlu dituju.1 s/foo/bar/
menggantikanfoo
pada baris 1 saja, jika ditemukan di sana. Jika demikian,t
cabang ke akhir skrip (melompati perintah yang tersisa di baris). (t
Fungsi bercabang ke label hanya jikas
panggilan terbaru melakukan substitusi yang sebenarnya; jika tidak ada label, seperti halnya di sini, akhir skrip bercabang ke).Ketika itu terjadi, alamat rentang
1,//
, yang biasanya menemukan kejadian pertama mulai dari baris 2 , tidak akan cocok, dan rentang tidak akan diproses, karena alamat dievaluasi ketika baris saat ini sudah2
.Sebaliknya, jika tidak ada kecocokan di baris 1,
1,//
akan dimasukkan, dan akan menemukan kecocokan pertama yang sebenarnya.Efek bersih adalah sama dengan GNU
sed
's0,/re/
: hanya kejadian pertama diganti, apakah itu terjadi di jalur 1 atau lainnya.Pendekatan non-jangkauan
jawaban potong menunjukkan teknik loop yang memotong kebutuhan untuk rentang ; karena dia menggunakan sintaksis GNU
sed
, berikut ini adalah padanan-POSIX-compliant :Teknik loop 1: Pada pertandingan pertama, lakukan substitusi, lalu masukkan loop yang hanya mencetak garis yang tersisa apa adanya :
Teknik loop 2, hanya untuk file bertubuh kecil : baca seluruh input ke dalam memori, kemudian lakukan penggantian tunggal di atasnya .
1 1.61803 memberikan contoh tentang apa yang terjadi dengan
1,/re/
, dengan dan tanpa yang berikuts//
:-
sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'
hasil$'1bar\n2bar'
; yaitu, kedua baris diperbarui, karena nomor baris1
cocok dengan baris 1, dan regex/foo/
- akhir rentang - kemudian hanya dicari untuk memulai pada baris berikutnya . Oleh karena itu, kedua jalur dipilih dalam kasus ini, dans/foo/bar/
penggantian dilakukan pada keduanya.-
sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo'
Gagal : dengansed: first RE may not be empty
(BSD / macOS) dansed: -e expression #1, char 0: no previous regular expression
(GNU), karena, pada saat baris pertama sedang diproses (karena nomor baris1
mulai rentang), belum ada regex yang diterapkan, jadi//
tidak mengacu pada apa pun.Dengan pengecualian sintaks
sed
khusus GNU0,/re/
, rentang apa pun yang dimulai dengan nomor baris secara efektif menghalangi penggunaan//
.sumber
Anda dapat menggunakan awk untuk melakukan hal serupa ..
Penjelasan:
Jalankan pernyataan tindakan antara {} ketika baris cocok dengan "#include" dan kami belum memprosesnya.
Ini mencetak #include "newfile.h", kita harus menghindari tanda kutip. Lalu kami mengatur variabel selesai ke 1, jadi kami tidak menambahkan lebih banyak menyertakan.
Ini berarti "print out the line" - tindakan kosong default untuk mencetak $ 0, yang mencetak seluruh baris. Satu kalimat dan lebih mudah dimengerti daripada IMO :-)
sumber
awk '/version/ && !done {print " \"version\": \"'${NEWVERSION}'\""; done=1;}; 1;' package.json
awk '/#include/ && !done { gsub(/#include/, "include \"newfile.h\""); done=1}; 1' file.c
Cukup banyak koleksi jawaban di linuxtopia dan FAQ . Ini juga menyoroti bahwa beberapa jawaban yang diberikan orang tidak akan berfungsi dengan versi sed non-GNU, misalnya
dalam versi non-GNU harus
Namun, versi ini tidak akan berfungsi dengan gnu sed.
Berikut ini adalah versi yang berfungsi dengan baik:
ex:
sumber
Cara kerja skrip ini: Untuk garis antara 1 dan yang pertama
#include
(setelah baris 1), jika baris dimulai dengan#include
, maka tambahkan baris yang ditentukan.Namun, jika yang pertama
#include
ada di baris 1, maka baris 1 dan selanjutnya berikutnya#include
akan memiliki baris yang di-prepended. Jika Anda menggunakan GNUsed
, ia memiliki ekstensi di mana0,/^#include/
(bukan1,
) akan melakukan hal yang benar.sumber
Tambahkan saja jumlah kejadian di akhir:
sumber
sed
menentukan perintah pengganti dengan:[2addr]s/BRE/replacement/flags
dan mencatat bahwa "Nilai bendera harus nol atau lebih dari: n Pengganti untuk kemunculan ke-n hanya BRE yang ditemukan dalam ruang pola." Jadi, setidaknya dalam POSIX 2008, trailing1
bukansed
ekstensi GNU . Memang, bahkan dalam standar SUS / POSIX 1997 , ini didukung, jadi saya sangat keluar dari jalur pada tahun 2008.Solusi yang mungkin:
Penjelasan:
sumber
sed: file me4.sed line 4: ":" lacks a label
Saya tahu ini posting lama tapi saya punya solusi yang biasa saya gunakan:
Pada dasarnya gunakan grep untuk mencetak kejadian pertama dan berhenti di situ. Selain itu cetak nomor jalur yaitu
5:line
. Pipa itu ke sed dan menghapus: dan apa pun setelah begitu Anda hanya tersisa dengan nomor baris. Pipa itu ke sed yang menambahkan s /.*/ ganti ke nomor akhir, yang menghasilkan skrip 1 baris yang disalurkan ke sed terakhir untuk dijalankan sebagai skrip pada file.jadi jika regex =
#include
dan ganti =blah
dan kejadian grep ditemukan pertama kali pada baris 5 maka data disalurkan ke sed terakhir5s/.*/blah/
.Bekerja bahkan jika kemunculan pertama ada di baris pertama.
sumber
sed -f -
yang tidak, tetapi Anda dapat mengatasinya :)Jika ada yang datang ke sini untuk mengganti karakter untuk kemunculan pertama di semua baris (seperti saya), gunakan ini:
Dengan mengubah 1 menjadi 2 misalnya, Anda dapat mengganti semua yang kedua saja.
sumber
's/a/b/'
berartimatch a
, dando just first match
for every matching line
Dengan
-z
opsi sed GNU Anda dapat memproses seluruh file seolah-olah hanya satu baris. Dengan cara itu as/…/…/
hanya akan mengganti kecocokan pertama di seluruh file. Ingat:s/…/…/
hanya mengganti kecocokan pertama di setiap baris, tetapi dengan-z
opsised
memperlakukan seluruh file sebagai satu baris.Dalam kasus umum Anda harus menulis ulang ekspresi sed Anda karena ruang pola sekarang menampung seluruh file, bukan hanya satu baris. Beberapa contoh:
s/text.*//
dapat ditulis ulang sebagais/text[^\n]*//
.[^\n]
cocok dengan semuanya kecuali karakter baris baru.[^\n]*
akan cocok dengan semua simbol setelahtext
sampai garis baru tercapai.s/^text//
dapat ditulis ulang sebagais/(^|\n)text//
.s/text$//
dapat ditulis ulang sebagais/text(\n|$)//
.sumber
saya akan melakukan ini dengan skrip awk:
kemudian jalankan dengan awk:
mungkin ceroboh, saya baru dalam hal ini.
sumber
Sebagai saran alternatif, Anda mungkin ingin melihat
ed
perintah.sumber
Saya akhirnya berhasil menggunakan skrip Bash yang digunakan untuk menyisipkan stempel waktu unik di setiap item dalam umpan RSS:
Ini mengubah kejadian pertama saja.
${nowms}
adalah waktu dalam milidetik yang ditetapkan oleh skrip Perl,$counter
adalah penghitung yang digunakan untuk kontrol loop dalam skrip,\
memungkinkan perintah untuk dilanjutkan pada baris berikutnya.File dibaca dalam dan stdout diarahkan ke file kerja.
Cara saya memahaminya,
1,/====RSSpermalink====/
memberi tahu kapan harus berhenti dengan menetapkan batasan rentang, dan kemudians/====RSSpermalink====/${nowms}/
adalah perintah sed yang sudah dikenal untuk mengganti string pertama dengan yang kedua.Dalam kasus saya, saya menempatkan perintah dalam tanda kutip ganda karena saya menggunakannya dalam skrip Bash dengan variabel.
sumber
Menggunakan FreeBSD
ed
dan hindaried
kesalahan "tidak cocok" jika tidak adainclude
pernyataan dalam file yang akan diproses:sumber
Ini mungkin bekerja untuk Anda (sed GNU):
atau jika memori tidak menjadi masalah:
sumber
Perintah berikut menghapus kemunculan pertama string, dalam file. Ini menghapus garis kosong juga. Itu disajikan pada file xml, tetapi akan bekerja dengan file apa pun.
Berguna jika Anda bekerja dengan file xml dan Anda ingin menghapus tag. Dalam contoh ini, ia menghapus kemunculan pertama tag "isTag".
Perintah:
File sumber (source.txt)
File hasil (output.txt)
ps: tidak berfungsi untuk saya di Solaris SunOS 5.10 (cukup lama), tetapi bekerja di Linux 2.6, versi sed 4.1.1
sumber
sed
(karenanya tidak bekerja dengan Solaris). Anda harus menghapus ini, tolong - itu benar-benar tidak memberikan informasi baru yang berbeda untuk sebuah pertanyaan yang sudah berumur 4½ tahun ketika Anda menjawab. Memang, memang ada contoh yang berhasil, tapi itu nilai yang dapat diperdebatkan ketika pertanyaan memiliki jawaban sebanyak ini.Tidak ada yang baru tapi mungkin jawaban yang lebih konkret:
sed -rn '0,/foo(bar).*/ s%%\1%p'
Contoh:
xwininfo -name unity-launcher
menghasilkan keluaran seperti:Mengekstraksi ID jendela dengan
xwininfo -name unity-launcher|sed -rn '0,/^xwininfo: Window id: (0x[0-9a-fA-F]+).*/ s%%\1%p'
menghasilkan:sumber
POSIXly (juga valid sed), Hanya satu regex yang digunakan, hanya membutuhkan memori untuk satu baris (seperti biasa):
Dijelaskan:
sumber
Kasus penggunaan mungkin adalah bahwa kejadian Anda tersebar di seluruh file Anda, tetapi Anda tahu satu-satunya kekhawatiran Anda adalah dalam 10, 20 atau 100 baris pertama.
Maka cukup dengan menekan garis-garis tersebut akan memperbaiki masalah - bahkan jika kata-kata OP hanya berlaku terlebih dahulu.
sumber
Solusi yang mungkin di sini adalah memberi tahu kompiler untuk memasukkan header tanpa disebutkan dalam file sumber. DI GCC ada beberapa opsi ini:
Kompiler Microsoft memiliki opsi / FI (terpaksa termasuk).
Fitur ini dapat berguna untuk beberapa header umum, seperti konfigurasi platform. Kernel Linux menggunakan Makefile
-include
untuk ini.sumber
sumber