Bagaimana cara menggunakan sed untuk mengganti hanya kejadian pertama dalam file?

217

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 sedmengganti 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.

David Dibben
sumber

Jawaban:

135
 # sed script to change "foo" to "bar" only on the first occurrence
 1{x;s/^/first/;x;}
 1,/foo/{x;/first/s///;x;s/foo/bar/;}
 #---end of script---

atau, jika Anda lebih suka: Catatan editor: hanya berfungsi dengan GNU sed .

sed '0,/foo/s//bar/' file 

Sumber

Ben Hoffstein
sumber
86
Saya pikir saya lebih suka solusi 'atau jika Anda lebih suka'. Akan lebih baik untuk menjelaskan jawaban - dan untuk membuat jawaban menjawab pertanyaan secara langsung, dan kemudian menggeneralisasi, daripada menggeneralisasi saja. Tapi jawaban yang bagus.
Jonathan Leffler
7
FYI untuk pengguna Mac, Anda harus mengganti 0 dengan 1, jadi: sed '1, / RE / s // to_that /' file
mhost
1
@ mhost Tidak, itu akan menggantikan jika pola ditemukan di baris # 1 dan tidak jika pola ada di baris lain, tetapi masih merupakan pola pertama yang ditemukan. PS harus disebutkan bahwa ini 0,hanya bekerja dengangnu sed
Jotne
11
Bisakah seseorang tolong menjelaskan solusi 'atau jika Anda lebih suka'? Saya tidak tahu harus meletakkan pola "dari" di mana.
Jean-Luc Nacif Coelho
3
@ Jean-LucNacifCoelho: using s//- ie, regex kosong - berarti regex yang paling baru diterapkan secara implisit digunakan kembali; dalam hal ini RE,. Pintasan praktis ini berarti Anda tidak perlu menduplikasi regex akhiran jangkauan dalam spanggilan Anda .
mklement0
289

Sebuah sedskrip yang hanya akan menggantikan kemunculan pertama "Apple" dengan "Banana"

Contoh

     Input:      Output:

     Apple       Banana
     Apple       Apple
     Orange      Orange
     Apple       Apple

Ini adalah skrip sederhana: Catatan editor: hanya berfungsi dengan GNU sed .

sed '0,/Apple/{s/Apple/Banana/}' input_filename

Dua parameter pertama 0dan /Apple/merupakan penentu rentang. Inilah s/Apple/Banana/yang dieksekusi dalam rentang itu. Jadi dalam hal ini "dalam rentang awal ( 0) hingga instance pertama Apple, ganti Appledengan Banana. Hanya yang pertama yang Appleakan diganti.

Latar Belakang: Secara tradisional sedrentang 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 ). GNUsed menambahkan ekstensi sendiri yang memungkinkan menetapkan awal sebagai "semu" line 0sehingga akhir rentang dapat line 1, memungkinkan rentang "hanya baris pertama"

Atau versi yang disederhanakan (RE seperti kosong //berarti menggunakan kembali yang ditentukan sebelumnya, jadi ini setara):

sed '0,/Apple/{s//Banana/}' input_filename

Dan kurung kurawal adalah opsional untuk sperintah, jadi ini juga setara:

sed '0,/Apple/s//Banana/' input_filename

Semua ini hanya berfungsi pada GNU sed.

Anda juga dapat menginstal sed GNU pada OS X menggunakan homebrew brew install gnu-sed.

tim
sumber
166
diterjemahkan ke dalam bahasa manusia: mulai dari baris 0, lanjutkan hingga Anda cocok dengan 'Apple', jalankan subtitusi dalam kurung keriting. cfr
mariotomo
8
Pada OS X, saya mendapatkansed: 1: "…": bad flag in substitute command: '}'
DAPAT DITERIMA
5
@ELLIOTTCABLE pada OS X, gunakan sed -e '1s/Apple/Banana/;t' -e '1,/Apple/s//Banana/'. Dari jawaban @ MikhailVS (saat ini) di bawah.
djb
8
Bekerja tanpa tanda kurung juga:sed '0,/foo/s/foo/bar/'
Innokenty
5
Saya mengerti sed: -e expression #1, char 3: unexpected , '' dengan ini
Jonathan
56
sed '0,/pattern/s/pattern/replacement/' filename

ini berhasil untuk saya.

contoh

sed '0,/<Menu>/s/<Menu>/<Menu><Menu>Sub menu<\/Menu>/' try.txt > abc.txt

Catatan editor: keduanya hanya bekerja dengan GNU sed .

Sushil
sumber
2
@Landys ini masih menggantikan instance di baris lain juga; bukan hanya contoh pertama
sarat
1
@sarat Ya, Anda benar. sed '1,/pattern/s/pattern/replacement/' filenamehanya 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.
Landys
45

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, atau zshdiasumsikan sebagai shell.


Hanya GNU sed :

Jawaban Ben Hoffstein menunjukkan kepada kita bahwa GNU menyediakan ekstensi untuk spesifikasi POSIXsed yang memungkinkan formulir 2-alamat berikut : 0,/re/( remewakili 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 cocok re- apakah reterjadi pada baris pertama atau pada baris berikutnya.

  • Bandingkan ini dengan formulir yang sesuai dengan POSIX 1,/re/, yang membuat rentang yang cocok dari baris pertama hingga dan termasuk baris yang cocok redengan baris berikutnya ; dengan kata lain: ini tidak akan mendeteksi kemunculan pertama suatu rekecocokan jika kebetulan terjadi pada baris pertama dan juga mencegah penggunaan steno// untuk penggunaan kembali regex yang terakhir digunakan (lihat poin berikutnya). 1

Jika Anda menggabungkan 0,/re/alamat dengan panggilan s/.../.../(substitusi) yang menggunakan ekspresi reguler yang sama , perintah Anda hanya akan melakukan substitusi pada baris pertama yang cocok re.
sedmenyediakan cara pintas yang nyaman untuk menggunakan kembali ekspresi reguler yang paling terakhir diterapkan : pasangan pembatas kosong// ,.

$ sed '0,/foo/ s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo' 
1st bar         # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo

Hanya fitur POSIX sedseperti BSD (macOS)sed (juga akan berfungsi dengan GNU sed ):

Karena 0,/re/tidak dapat digunakan dan formulir 1,/re/tidak akan mendeteksi rejika 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:

$ sed -e '1 s/foo/bar/; t' -e '1,// s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar         # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo

catatan:

  • //Pintasan regex kosong digunakan dua kali di sini: sekali untuk titik akhir rentang, dan sekali dalam spanggilan; dalam kedua kasus, regex foosecara implisit digunakan kembali, memungkinkan kita tidak harus menduplikatnya, yang membuat kode menjadi lebih pendek dan lebih mudah dikelola.

  • POSIX sedmembutuhkan baris baru aktual setelah fungsi tertentu, seperti setelah nama label atau bahkan penghilangannya, seperti halnya di tsini; memisahkan skrip secara strategis menjadi beberapa -eopsi merupakan alternatif untuk menggunakan baris baru yang sebenarnya: akhiri setiap -epotongan skrip di mana baris baru biasanya perlu dituju.

1 s/foo/bar/menggantikan foopada baris 1 saja, jika ditemukan di sana. Jika demikian, tcabang ke akhir skrip (melompati perintah yang tersisa di baris). ( tFungsi bercabang ke label hanya jika spanggilan 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 sudah 2.

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's 0,/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 :

$ sed -e '/foo/ {s//bar/; ' -e ':a' -e '$!{n;ba' -e '};}' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo

Teknik loop 2, hanya untuk file bertubuh kecil : baca seluruh input ke dalam memori, kemudian lakukan penggantian tunggal di atasnya .

$ sed -e ':a' -e '$!{N;ba' -e '}; s/foo/bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo

1 1.61803 memberikan contoh tentang apa yang terjadi dengan 1,/re/, dengan dan tanpa yang berikut s//:
- sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'hasil $'1bar\n2bar'; yaitu, kedua baris diperbarui, karena nomor baris 1cocok 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, dan s/foo/bar/penggantian dilakukan pada keduanya.
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo' Gagal : dengan sed: first RE may not be empty(BSD / macOS) dan sed: -e expression #1, char 0: no previous regular expression(GNU), karena, pada saat baris pertama sedang diproses (karena nomor baris 1mulai rentang), belum ada regex yang diterapkan, jadi//tidak mengacu pada apa pun.
Dengan pengecualian sintaks sedkhusus GNU 0,/re/, rentang apa pun yang dimulai dengan nomor baris secara efektif menghalangi penggunaan //.

mklement0
sumber
25

Anda dapat menggunakan awk untuk melakukan hal serupa ..

awk '/#include/ && !done { print "#include \"newfile.h\""; done=1;}; 1;' file.c

Penjelasan:

/#include/ && !done

Jalankan pernyataan tindakan antara {} ketika baris cocok dengan "#include" dan kami belum memprosesnya.

{print "#include \"newfile.h\""; done=1;}

Ini mencetak #include "newfile.h", kita harus menghindari tanda kutip. Lalu kami mengatur variabel selesai ke 1, jadi kami tidak menambahkan lebih banyak menyertakan.

1;

Ini berarti "print out the line" - tindakan kosong default untuk mencetak $ 0, yang mencetak seluruh baris. Satu kalimat dan lebih mudah dimengerti daripada IMO :-)

richq
sumber
4
Jawaban ini lebih portabel daripada solusi sed, yang bergantung pada gnu sed dll. (Mis. Sed di OS-X menyebalkan!)
Jay Taylor
1
Ini benar-benar lebih dapat dimengerti, tetapi bagi saya itu menambahkan garis alih-alih menggantinya; perintah yang digunakan: awk '/version/ && !done {print " \"version\": \"'${NEWVERSION}'\""; done=1;}; 1;' package.json
Cereal Killer
sama di sini, perintah yang paling bisa dimengerti, tetapi menambahkan garis di atas string yang ditemukan alih-alih menggantinya
Flion
1
Jawabannya cukup mudah dibaca. Inilah versi saya yang menggantikan string alih-alih menambahkan baris baru. awk '/#include/ && !done { gsub(/#include/, "include \"newfile.h\""); done=1}; 1' file.c
Orsiris de Jong
18

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

sed '0,/RE/s//to_that/' file

dalam versi non-GNU harus

sed -e '1s/RE/to_that/;t' -e '1,/RE/s//to_that/'

Namun, versi ini tidak akan berfungsi dengan gnu sed.

Berikut ini adalah versi yang berfungsi dengan baik:

-e '/RE/{s//to_that/;:a' -e '$!N;$!ba' -e '}'

ex:

sed -e '/Apple/{s//Banana/;:a' -e '$!N;$!ba' -e '}' filename
MikhailVS
sumber
Memang, diuji bekerja pada Ubuntu Linux v16 dan FreeBSD v10.2. Terimakasih.
Sopalajo de Arrierez
12
#!/bin/sed -f
1,/^#include/ {
    /^#include/i\
#include "newfile.h"
}

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 #includeada di baris 1, maka baris 1 dan selanjutnya berikutnya #includeakan memiliki baris yang di-prepended. Jika Anda menggunakan GNU sed, ia memiliki ekstensi di mana 0,/^#include/(bukan 1,) akan melakukan hal yang benar.

Chris Jester-Young
sumber
11

Tambahkan saja jumlah kejadian di akhir:

sed s/#include/#include "newfile.h"\n#include/1
tidak ada
sumber
7
Sayangnya, ini tidak berhasil. Ini menggantikan kejadian pertama pada setiap baris file dan bukan kejadian pertama dalam file.
David Dibben
1
Selain itu, ini adalah ekstensi sed GNU, bukan fitur sed standar.
Jonathan Leffler
10
Hmmm ... waktu berlalu. POSIX 2008/2013 untuk sedmenentukan perintah pengganti dengan: [2addr]s/BRE/replacement/flagsdan 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, trailing 1bukan sedekstensi GNU . Memang, bahkan dalam standar SUS / POSIX 1997 , ini didukung, jadi saya sangat keluar dari jalur pada tahun 2008.
Jonathan Leffler
7

Solusi yang mungkin:

    /#include/!{p;d;}
    i\
    #include "newfile.h"
    :a
    n
    ba

Penjelasan:

  • baca baris sampai kita menemukan #include, cetak baris ini lalu mulailah siklus baru
  • masukkan baris include baru
  • masukkan loop yang hanya membaca baris (secara default sed juga akan mencetak baris ini), kita tidak akan kembali ke bagian pertama dari skrip dari sini
mitchnull
sumber
sed: file me4.sed line 4: ":" lacks a label
rogerdpack
rupanya ada yang berubah di versi sed terbaru dan label kosong tidak lagi diizinkan. memperbarui jawabannya
mitchnull
4

Saya tahu ini posting lama tapi saya punya solusi yang biasa saya gunakan:

grep -E -m 1 -n 'old' file | sed 's/:.*$//' - | sed 's/$/s\/old\/new\//' - | sed -f - file

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 = #includedan ganti = blahdan kejadian grep ditemukan pertama kali pada baris 5 maka data disalurkan ke sed terakhir 5s/.*/blah/.

Bekerja bahkan jika kemunculan pertama ada di baris pertama.

Michael Edwards
sumber
Saya benar-benar membenci skrip multiline sed atau perintah sed dengan apa pun kecuali s dan linenumber jadi saya setuju dengan pendekatan ini. Inilah yang saya gunakan untuk usecase saya (berfungsi dengan bash): filepath = / etc / hosts; patt = '^ \ (127 \ .0 \ .0 \ .1. * \)'; repl = '\ 1 newhostalias'; sed $ (IFS =: linearray = ($ (grep -E -m 1 -n "$ patt" "$ filepath")) && echo $ {linearray [0]}) s / "$ patt" / "$ repl" / "$ filepath"
parity3
Berhasil. Meskipun hanya dengan sed yang cukup pintar untuk menerima sed -f -yang tidak, tetapi Anda dapat mengatasinya :)
rogerdpack
3

Jika ada yang datang ke sini untuk mengganti karakter untuk kemunculan pertama di semua baris (seperti saya), gunakan ini:

sed '/old/s/old/new/1' file

-bash-4.2$ cat file
123a456a789a
12a34a56
a12
-bash-4.2$ sed '/a/s/a/b/1' file
123b456a789a
12b34a56
b12

Dengan mengubah 1 menjadi 2 misalnya, Anda dapat mengganti semua yang kedua saja.

FatihSarigol
sumber
2
Anda tidak perlu melakukan semua ini, 's/a/b/'berarti match a, dando just first match for every matching line
Samveen
Saya dengan Samveen. Juga ini tidak menjawab pertanyaan yang diajukan di sini . Saya akan menyarankan untuk menghapus jawaban ini.
Socowi
Pertanyaannya adalah "kemunculan pertama dalam file" bukan "kemunculan pertama dalam satu baris"
Manuel Romeiro
3

Dengan -zopsi sed GNU Anda dapat memproses seluruh file seolah-olah hanya satu baris. Dengan cara itu a s/…/…/hanya akan mengganti kecocokan pertama di seluruh file. Ingat: s/…/…/hanya mengganti kecocokan pertama di setiap baris, tetapi dengan -zopsi sedmemperlakukan seluruh file sebagai satu baris.

sed -z 's/#include/#include "newfile.h"\n#include'

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 sebagai s/text[^\n]*//. [^\n]cocok dengan semuanya kecuali karakter baris baru. [^\n]*akan cocok dengan semua simbol setelah textsampai garis baru tercapai.
  • s/^text//dapat ditulis ulang sebagai s/(^|\n)text//.
  • s/text$//dapat ditulis ulang sebagai s/text(\n|$)//.
Socowi
sumber
2

saya akan melakukan ini dengan skrip awk:

BEGIN {i=0}
(i==0) && /#include/ {print "#include \"newfile.h\""; i=1}
{print $0}    
END {}

kemudian jalankan dengan awk:

awk -f awkscript headerfile.h > headerfilenew.h

mungkin ceroboh, saya baru dalam hal ini.

wakingrufus
sumber
2

Sebagai saran alternatif, Anda mungkin ingin melihat edperintah.

man 1 ed

teststr='
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
'

# for in-place file editing use "ed -s file" and replace ",p" with "w"
# cf. http://wiki.bash-hackers.org/howto/edit-ed
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s <(echo "$teststr")
   H
   /# *include/i
   #include "newfile.h"
   .
   ,p
   q
EOF
timo
sumber
2

Saya akhirnya berhasil menggunakan skrip Bash yang digunakan untuk menyisipkan stempel waktu unik di setiap item dalam umpan RSS:

        sed "1,/====RSSpermalink====/s/====RSSpermalink====/${nowms}/" \
            production-feed2.xml.tmp2 > production-feed2.xml.tmp.$counter

Ini mengubah kejadian pertama saja.

${nowms}adalah waktu dalam milidetik yang ditetapkan oleh skrip Perl, $counteradalah 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 kemudian s/====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.

Michael Cook
sumber
2

Menggunakan FreeBSD ed dan hindari edkesalahan "tidak cocok" jika tidak ada includepernyataan dalam file yang akan diproses:

teststr='
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
'

# using FreeBSD ed
# to avoid ed's "no match" error, see
# *emphasized text*http://codesnippets.joyent.com/posts/show/11917 
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s <(echo "$teststr")
   H
   ,g/# *include/u\
   u\
   i\
   #include "newfile.h"\
   .
   ,p
   q
EOF
nazq
sumber
Hal ini sangat mirip dengan timo 's jawaban tetapi ditambahkan lebih dari setahun kemudian.
Jonathan Leffler
2

Ini mungkin bekerja untuk Anda (sed GNU):

sed -si '/#include/{s//& "newfile.h\n&/;:a;$!{n;ba}}' file1 file2 file....

atau jika memori tidak menjadi masalah:

sed -si ':a;$!{N;ba};s/#include/& "newfile.h\n&/' file1 file2 file...
potong
sumber
0

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:

sed -e 0,/'<isTag>false<\/isTag>'/{s/'<isTag>false<\/isTag>'//}  -e 's/ *$//' -e  '/^$/d'  source.txt > output.txt

File sumber (source.txt)

<xml>
    <testdata>
        <canUseUpdate>true</canUseUpdate>
        <isTag>false</isTag>
        <moduleLocations>
            <module>esa_jee6</module>
            <isTag>false</isTag>
        </moduleLocations>
        <node>
            <isTag>false</isTag>
        </node>
    </testdata>
</xml>

File hasil (output.txt)

<xml>
    <testdata>
        <canUseUpdate>true</canUseUpdate>
        <moduleLocations>
            <module>esa_jee6</module>
            <isTag>false</isTag>
        </moduleLocations>
        <node>
            <isTag>false</isTag>
        </node>
    </testdata>
</xml>

ps: tidak berfungsi untuk saya di Solaris SunOS 5.10 (cukup lama), tetapi bekerja di Linux 2.6, versi sed 4.1.1

Andreas Panagiotidis
sumber
Ini terlihat seperti ide dasar yang sama dengan sejumlah jawaban sebelumnya, dengan peringatan yang sama bahwa ia hanya bekerja dengan GNU 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.
Jonathan Leffler
0

Tidak ada yang baru tapi mungkin jawaban yang lebih konkret: sed -rn '0,/foo(bar).*/ s%%\1%p'

Contoh: xwininfo -name unity-launchermenghasilkan keluaran seperti:

xwininfo: Window id: 0x2200003 "unity-launcher"

  Absolute upper-left X:  -2980
  Absolute upper-left Y:  -198
  Relative upper-left X:  0
  Relative upper-left Y:  0
  Width: 2880
  Height: 98
  Depth: 24
  Visual: 0x21
  Visual Class: TrueColor
  Border width: 0
  Class: InputOutput
  Colormap: 0x20 (installed)
  Bit Gravity State: ForgetGravity
  Window Gravity State: NorthWestGravity
  Backing Store State: NotUseful
  Save Under State: no
  Map State: IsViewable
  Override Redirect State: no
  Corners:  +-2980+-198  -2980+-198  -2980-1900  +-2980-1900
  -geometry 2880x98+-2980+-198

Mengekstraksi ID jendela dengan xwininfo -name unity-launcher|sed -rn '0,/^xwininfo: Window id: (0x[0-9a-fA-F]+).*/ s%%\1%p'menghasilkan:

0x2200003
Stephen Niedzielski
sumber
0

POSIXly (juga valid sed), Hanya satu regex yang digunakan, hanya membutuhkan memori untuk satu baris (seperti biasa):

sed '/\(#include\).*/!b;//{h;s//\1 "newfile.h"/;G};:1;n;b1'

Dijelaskan:

sed '
/\(#include\).*/!b          # Only one regex used. On lines not matching
                            # the text  `#include` **yet**,
                            # branch to end, cause the default print. Re-start.
//{                         # On first line matching previous regex.
    h                       # hold the line.
    s//\1 "newfile.h"/      # append ` "newfile.h"` to the `#include` matched.
    G                       # append a newline.
  }                         # end of replacement.
:1                          # Once **one** replacement got done (the first match)
n                           # Loop continually reading a line each time
b1                          # and printing it by default.
'                           # end of sed script.
Ishak
sumber
0

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.

sed '1,10s/#include/#include "newfile.h"\n#include/'
sastorsl
sumber
0

Solusi yang mungkin di sini adalah memberi tahu kompiler untuk memasukkan header tanpa disebutkan dalam file sumber. DI GCC ada beberapa opsi ini:

   -include file
       Process file as if "#include "file"" appeared as the first line of
       the primary source file.  However, the first directory searched for
       file is the preprocessor's working directory instead of the
       directory containing the main source file.  If not found there, it
       is searched for in the remainder of the "#include "..."" search
       chain as normal.

       If multiple -include options are given, the files are included in
       the order they appear on the command line.

   -imacros file
       Exactly like -include, except that any output produced by scanning
       file is thrown away.  Macros it defines remain defined.  This
       allows you to acquire all the macros from a header without also
       processing its declarations.

       All files specified by -imacros are processed before all files
       specified by -include.

Kompiler Microsoft memiliki opsi / FI (terpaksa termasuk).

Fitur ini dapat berguna untuk beberapa header umum, seperti konfigurasi platform. Kernel Linux menggunakan Makefile -includeuntuk ini.

Kaz
sumber
-1
sed -e 's/pattern/REPLACEMENT/1' <INPUTFILE
warhansen
sumber