Saya ingin mengganti hanya kcontoh kata pertama.
Bagaimana saya bisa melakukan ini?
Misalnya. Katakanlah file foo.txtberisi 100 kejadian kata 'linux'.
Saya perlu mengganti 50 kejadian pertama saja.
Saya ingin mengganti hanya kcontoh kata pertama.
Bagaimana saya bisa melakukan ini?
Misalnya. Katakanlah file foo.txtberisi 100 kejadian kata 'linux'.
Saya perlu mengganti 50 kejadian pertama saja.
Jawaban:
Bagian pertama di bawah ini menjelaskan penggunaan seduntuk mengubah kejadian-k pertama pada suatu garis. Bagian kedua memperluas pendekatan ini untuk mengubah hanya kejadian-k pertama dalam file, terlepas dari apa baris mereka muncul.
Dengan sed standar, ada perintah untuk mengganti kemunculan kata ke-k pada sebuah baris. Jika k3, misalnya:
sed 's/old/new/3'
Atau, seseorang dapat mengganti semua kejadian dengan:
sed 's/old/new/g'
Tidak satu pun dari ini yang Anda inginkan.
GNU sedmenawarkan ekstensi yang akan mengubah kejadian k-th dan semuanya setelah itu. Jika k adalah 3, misalnya:
sed 's/old/new/g3'
Ini dapat digabungkan untuk melakukan apa yang Anda inginkan. Untuk mengubah 3 kejadian pertama:
$ echo old old old old old | sed -E 's/\<old\>/\n/g4; s/\<old\>/new/g; s/\n/old/g'
new new new old old
di mana \nberguna di sini karena kita dapat yakin bahwa itu tidak pernah terjadi pada suatu garis.
Kami menggunakan tiga sedperintah substitusi:
s/\<old\>/\n/g4
Ini ekstensi GNU untuk menggantikan yang keempat dan semua kejadian berikutnya olddengan \n.
Fitur regex diperpanjang \<digunakan untuk mencocokkan awal kata dan \>untuk mencocokkan akhir kata. Ini memastikan bahwa hanya kata-kata lengkap yang cocok. Regex yang diperluas membutuhkan -Eopsi untuk sed.
s/\<old\>/new/g
Hanya tiga kejadian pertama yang oldtersisa dan ini menggantikan semuanya new.
s/\n/old/g
Kejadian keempat dan semua yang tersisa olddigantikan dengan \npada langkah pertama. Ini mengembalikan mereka ke keadaan semula.
Jika GNU sed tidak tersedia dan Anda ingin mengubah 3 kejadian pertama oldmenjadi new, maka gunakan tiga sperintah:
$ echo old old old old old | sed -E -e 's/\<old\>/new/' -e 's/\<old\>/new/' -e 's/\<old\>/new/'
new new new old old
Ini bekerja dengan baik ketika ksejumlah kecil tetapi skala buruk ke besar k.
Karena beberapa sed non-GNU tidak mendukung menggabungkan perintah dengan titik koma, setiap perintah di sini diperkenalkan dengan -eopsi sendiri . Mungkin juga perlu untuk memverifikasi bahwa Anda sedmendukung simbol batas kata, \<dan \>.
Kita dapat meminta sed untuk membaca seluruh file dan kemudian melakukan penggantian. Misalnya, untuk mengganti tiga kejadian pertama oldmenggunakan sed gaya BSD:
sed -E -e 'H;1h;$!d;x' -e 's/\<old\>/new/' -e 's/\<old\>/new/' -e 's/\<old\>/new/'
Perintah sed H;1h;$!d;xmembaca seluruh file di.
Karena di atas tidak menggunakan ekstensi GNU, itu harus bekerja pada BSD (OSX) sed. Perhatikan, pikirkan, bahwa pendekatan ini membutuhkan sedyang dapat menangani garis panjang. GNU sedseharusnya baik-baik saja. Mereka yang menggunakan versi non-GNU sedharus menguji kemampuannya untuk menangani antrean panjang.
Dengan sed GNU, kita dapat lebih lanjut menggunakan gtrik yang dijelaskan di atas, tetapi dengan \ndiganti dengan \x00, untuk mengganti tiga kejadian pertama:
sed -E -e 'H;1h;$!d;x; s/\<old\>/\x00/g4; s/\<old\>/new/g; s/\x00/old/g'
Pendekatan ini berskala juga kmenjadi besar. Ini mengasumsikan, bahwa \x00itu tidak ada dalam string asli Anda. Karena tidak mungkin untuk menempatkan karakter \x00dalam string bash, ini biasanya merupakan asumsi yang aman.
tr '\n' '|' < input_file | sed …. Tetapi, tentu saja, itu mengubah seluruh input menjadi satu baris, dan beberapa sed non-GNU tidak dapat menangani garis panjang yang sewenang-wenang. (2) Anda berkata, "... di atas, string yang dikutip '|'harus diganti oleh karakter apa pun, atau string karakter, ..." Tetapi Anda tidak dapat menggunakan truntuk mengganti karakter dengan string (panjang> 1). (3) Dalam contoh terakhir Anda, Anda katakan -e 's/\<old\>/new/' -e 's/\<old\>/w/' | tr '\000' '\n'\>/new. Ini sepertinya salah ketik untuk -e 's/\<old\>/new/' -e 's/\<old\>/new/' -e 's/\<old\>/new/' | tr '\000' '\n'.
Perintah awk dapat digunakan untuk mengganti N kejadian pertama kata dengan penggantian.
Perintah hanya akan menggantikan jika kata tersebut benar-benar cocok.
Dalam contoh di bawah ini, saya mengganti 27kejadian pertama olddengannew
Menggunakan sub
awk '{for(i=1;i<=NF;i++){if(x<27&&$i=="old"){x++;sub("old","new",$i)}}}1' file
Perintah ini melewati setiap bidang hingga cocok
old, memeriksa penghitung di bawah 27, peningkatan dan mengganti kecocokan pertama pada baris. Kemudian pindah ke bidang / baris berikutnya dan ulangi.
Mengganti bidang secara manual
awk '{for(i=1;i<=NF;i++)if(x<27&&$i=="old"&&$i="new")x++}1' file
Mirip dengan perintah sebelumnya tetapi karena sudah memiliki penanda di bidang mana itu hingga
($i), itu hanya mengubah nilai bidang darioldkenew.
Melakukan pemeriksaan sebelumnya
awk '/old/&&x<27{for(i=1;i<=NF;i++)if(x<27&&$i=="old"&&$i="new")x++}1' file
Memeriksa bahwa baris berisi yang lama dan penghitung di bawah 27
SHOULDmemberikan dorongan kecepatan kecil karena tidak akan memproses garis ketika ini salah.
HASIL
Misalnya
old bold old old old
old old nold old old
old old old gold old
old gold gold old old
old old old man old old
old old old old dog old
old old old old say old
old old old old blah old
untuk
new bold new new new
new new nold new new
new new new gold new
new gold gold new new
new new new man new new
new new new new dog new
new new old old say old
old old old old blah old
Katakanlah Anda ingin mengganti hanya tiga contoh pertama dari string ...
seq 11 100 311 |
sed -e 's/1/\
&/g' \ #s/match string/\nmatch string/globally
-e :t \ #define label t
-e '/\n/{ x' \ #newlines must match - exchange hold and pattern spaces
-e '/.\{3\}/!{' \ #if not 3 characters in hold space do
-e 's/$/./' \ #add a new char to hold space
-e x \ #exchange hold/pattern spaces again
-e 's/\n1/2/' \ #replace first occurring '\n1' string w/ '2' string
-e 'b t' \ #branch back to label t
-e '};x' \ #end match function; exchange hold/pattern spaces
-e '};s/\n//g' #end match function; remove all newline characters
catatan: di atas kemungkinan tidak akan berfungsi dengan komentar yang disematkan
... atau dalam contoh kasus saya, dari '1' ...
22
211
211
311
Di sana saya menggunakan dua teknik penting. Di tempat pertama setiap kemunculan 1pada satu baris diganti dengan \n1. Dengan cara ini, ketika saya melakukan penggantian rekursif berikutnya, saya bisa pastikan untuk tidak mengganti kejadian dua kali jika string pengganti saya berisi string pengganti saya. Misalnya, jika saya ganti hedengan heyitu masih akan berfungsi.
Saya melakukan ini seperti:
s/1/\
&/g
Kedua, saya menghitung penggantian dengan menambahkan karakter ke hruang lama untuk setiap kejadian. Begitu saya mencapai tiga tidak ada lagi terjadi. Jika Anda menerapkan ini pada data Anda dan mengubah \{3\}ke penggantian total yang Anda inginkan dan /\n1/alamat untuk apa pun yang Anda ingin ganti, Anda harus mengganti hanya sebanyak yang Anda inginkan.
Saya hanya melakukan semua -ehal untuk dibaca. POSIXly Dapat ditulis seperti ini:
nl='
'; sed "s/1/\\$nl&/g;:t${nl}/\n/{x;/.\{3\}/!{${nl}s/$/./;x;s/\n1/2/;bt$nl};x$nl};s/\n//g"
Dan dengan GNU sed:
sed 's/1/\n&/g;:t;/\n/{x;/.\{3\}/!{s/$/./;x;s/\n1/2/;bt};x};s/\n//g'
Ingat juga bahwa sedini berorientasi garis - tidak membaca di seluruh file dan kemudian mencoba untuk mengulanginya seperti yang sering terjadi pada editor lain. sedsederhana dan efisien. Yang mengatakan, sering kali nyaman untuk melakukan sesuatu seperti berikut:
Berikut adalah fungsi shell kecil yang membundelnya menjadi perintah yang dieksekusi sederhana:
firstn() { sed "s/$2/\
&/g;:t
/\n/{x
/.\{$(($1))"',\}/!{
s/$/./; x; s/\n'"$2/$3"'/
b t
};x
};s/\n//g'; }
Maka dengan itu saya bisa melakukan:
seq 11 100 311 | firstn 7 1 5
... dan dapatkan ...
55
555
255
311
...atau...
seq 10 1 25 | firstn 6 '\(.\)\([1-5]\)' '\15\2'
...mendapatkan...
10
151
152
153
154
155
16
17
18
19
20
251
22
23
24
25
... atau, untuk mencocokkan contoh Anda (dengan urutan yang lebih kecil) :
yes linux | head -n 10 | firstn 5 linux 'linux is an os kernel'
linux is an os kernel
linux is an os kernel
linux is an os kernel
linux is an os kernel
linux is an os kernel
linux
linux
linux
linux
linux
Alternatif singkat di Perl:
perl -pe 'BEGIN{$n=3} 1 while s/old/new/ && ++$i < $n' your_file
Ubah nilai `$ n $ sesuai keinginan Anda.
Bagaimana itu bekerja:
newuntuk old( s/old/new/) dan kapan pun bisa, itu akan menambahkan variabel $i( ++$i).1 while ...) selama itu telah membuat kurang dari $ntotal substitusi dan dapat membuat setidaknya satu substitusi pada baris itu.Gunakan loop shell dan ex!
{ for i in {1..50}; do printf %s\\n '0/old/s//new/'; done; echo x;} | ex file.txt
Ya, ini agak konyol.
;)
Catatan: Ini mungkin gagal jika ada kurang dari 50 contoh olddalam file. (Saya belum mengujinya.) Jika demikian, itu akan membuat file tidak dimodifikasi.
Lebih baik lagi, gunakan Vim.
vim file.txt
qqgg/old<CR>:s/old/new/<CR>q49@q
:x
Penjelasan:
q # Start recording macro
q # Into register q
gg # Go to start of file
/old<CR> # Go to first instance of 'old'
:s/old/new/<CR> # Change it to 'new'
q # Stop recording
49@q # Replay macro 49 times
:x # Save and exit
Solusi sederhana, tetapi tidak terlalu cepat adalah untuk mengulang perintah yang dijelaskan dalam /programming/148451/how-to-use-sed-to-replace-only-the-first-occurrence-in-a -mengajukan
for i in $(seq 50) ; do sed -i -e "0,/oldword/s//newword/" file.txt ; done
Perintah sed khusus ini mungkin hanya berfungsi untuk GNU sed dan jika newword bukan bagian dari oldword . Untuk non-GNU lihat di sini cara mengganti hanya pola pertama dalam file.
Dengan GNU awkAnda dapat mengatur pemisah rekaman RSke kata yang akan diganti dibatasi oleh batas kata. Maka itu adalah kasus pengaturan pemisah rekaman pada output ke kata pengganti untuk kcatatan pertama sambil mempertahankan pemisah rekaman asli untuk sisanya.
awk -vRS='\\ylinux\\y' -vreplacement=unix -vlimit=50 \
'{printf "%s%s", $0, NR <= limit? replacement: RT}' file
ATAU
awk -vRS='\\ylinux\\y' -vreplacement=unix -vlimit=50 \
'{printf "%s%s", $0, limit--? replacement: RT}' file