Temukan dan ganti dalam file dan timpa file tidak berfungsi, itu mengosongkan file

604

Saya ingin menjalankan pencarian dan mengganti file HTML melalui baris perintah.

Perintah saya terlihat seperti ini:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html > index.html

Ketika saya menjalankan ini dan melihat file sesudahnya, itu kosong. Itu menghapus isi file saya.

Ketika saya menjalankan ini setelah mengembalikan file lagi:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

Ini stdoutadalah isi file, dan menemukan dan mengganti telah dieksekusi.

Mengapa ini terjadi?

BBales
sumber
13
Alternatif Perl:perl -pi -w -e 's/STRING_TO_REPLACE/REPLACE_WITH/g;' index.html
Gjorgji Tashkovski
banyak sedperintah terkait untuk menemukan string dan mengganti seluruh baris: stackoverflow.com/questions/11245144/…
cregox

Jawaban:

917

Ketika shell melihat > index.htmldi baris perintah itu membuka file index.htmluntuk menulis , menghapus semua konten sebelumnya.

Untuk memperbaikinya, Anda perlu memberikan -iopsi untuk sedmembuat perubahan sebaris dan membuat cadangan file asli sebelum melakukan perubahan di tempat:

sed -i.bak s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

Tanpa .bak perintah akan gagal pada beberapa platform, seperti Mac OSX.

codaddict
sumber
20
Mengatakan truncates the filebukannya opens the filemungkin membuatnya lebih jelas.
Mikel
12
Setidaknya di mac saya, saran pertama tidak berfungsi ... jika Anda melakukan penggantian di tempat pada file, Anda harus menentukan ekstensi. Anda dapat, setidaknya, meneruskan ekstensi dengan panjang nol: sed -i 's / STRING_TO_REPLACE / STRING_TO_REPLACE_IT / g index.html
Tom Lianza
5
untuk variabel sed -i.bak 's /' $ search '/' $ replace '/ g' index.html
Fatima Zohra
33
pada osx, gunakan string kosong '' sebagai parameter untuk -i, seperti:sed -i '' 's/blah/xx/g'
Pierre Houston
4
tapi apa milikmu .baksetelah ini sed -i?
Patrizio Bertoni
210

Alternatif, berguna, polanya adalah:

sed -e 'script script' index.html > index.html.tmp && mv index.html.tmp index.html

Itu memiliki banyak efek yang sama, tanpa menggunakan -iopsi, dan juga berarti bahwa, jika skrip sed gagal karena suatu alasan, file input tidak musnah. Selanjutnya, jika pengeditan berhasil, tidak ada file cadangan yang tertinggal. Idioma semacam ini dapat berguna di Makefiles.

Cukup banyak sed yang memiliki -ipilihan, tetapi tidak semuanya; sed posix adalah salah satu yang tidak. Jika Anda menginginkan portabilitas, maka sebaiknya dihindari.

Norman Gray
sumber
9
+1 untuk tanpa file cadangan yang diletakkan di sekeliling dan tidak merusak file input jika edit gagal. Bekerja dengan sempurna di mac.
Mike Grace
Bekerja untuk saya dengan sempurna. Terima kasih! (pada Mac)
tertarik
1
Ini bekerja dengan baik bagi saya di mana pada Ubuntu Server 14.04 sed-i terus memusatkan perhatian pada file.
Chris Giddings
2
Peningkatan yang sangat kecil:... && mv index.html{.tmp,}
EdwardGarson
5
@ EdwardGarson Memang, itu mungkin yang akan saya gunakan jika saya mengetik - Saya setuju itu lebih rapi - tetapi sh(jika saya ingat dengan benar) tidak memiliki {...}ekspansi itu. Dalam Makefile Anda mungkin menggunakan shdaripada bash, jadi jika Anda bertujuan untuk portabilitas (atau kemungkinan) maka Anda harus menghindari konstruksi itu.
Norman Gray
95
sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' index.html

Ini melakukan substitusi global di tempat pada file index.html. Mengutip string mencegah masalah dengan spasi putih dalam permintaan dan penggantian.

Apodaca kaya
sumber
57

gunakan opsi sed's -i, mis

sed -i bak -e s/STRING_TO_REPLACE/REPLACE_WITH/g index.html
Kevin
sumber
Apa artinya ini? sed: -i tidak boleh digunakan dengan stdin
sheetal
2
Ingat untuk mengelilingi pola Anda dalam tanda kutip jika berisi spasi -'s/STRING_TO_REPLACE/REPLACE_WITH/g'
Doug Thompson
@sheetal: -imelakukan pengeditan di tempat file , jadi tidak masuk akal untuk menggabungkannya dengan input stdin .
mklement0
Ini mungkin berfungsi pada macOS, tetapi tidak di Arch Linux untuk saya.
xdevs23
Tanpa -e, jawaban yang diterima tidak berfungsi pada MacOS, Catalina. Dengan -e itu berhasil.
cwhiii
18

Untuk mengubah banyak file (dan menyimpan cadangan masing-masing sebagai * .bak):

perl -p -i -e "s/\|/x/g" *  

akan mengambil semua file dalam direktori dan menggantinya |dengan x ini disebut "Perl pie" (semudah pie)

Stenemo
sumber
1
Senang melihat seseorang yang bersedia melihat pernyataan masalah, dan bukan hanya tag. OP tidak menentukan sedsebagai persyaratan, hanya menggunakannya sebagai alat yang sudah dicoba.
user7412956
14

Anda harus mencoba menggunakan opsi -iuntuk mengedit di tempat.

uloBasEI
sumber
6
sed -i.bak "s#https.*\.com#$pub_url#g" MyHTMLFile.html

Jika Anda memiliki tautan untuk ditambahkan, coba ini. Cari URL seperti di atas (dimulai dengan https dan berakhir dengan.com di sini) dan ganti dengan string URL. Saya telah menggunakan variabel di $pub_urlsini. sdi sini berarti pencarian dan gberarti penggantian global.

Berhasil !

Kaey
sumber
6

Peringatan: ini adalah metode yang berbahaya! Itu menyalahgunakan buffer i / o di linux dan dengan opsi spesifik buffering berhasil bekerja pada file kecil. Ini merupakan keingintahuan yang menarik. Tetapi jangan menggunakannya untuk situasi nyata!

Selain -iopsi sed Anda dapat menggunakan teeutilitas .

Dari man:

tee - baca dari input standar dan tulis ke output dan file standar

Jadi, solusinya adalah:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee | tee index.html

- di sini teediulang untuk memastikan bahwa pipa disangga. Kemudian semua perintah dalam pipa diblokir sampai mereka mendapatkan beberapa input untuk bekerja. Setiap perintah dalam pipa dimulai ketika perintah hulu telah menulis 1 buffer byte (ukurannya ditentukan di suatu tempat ) ke input perintah. Jadi perintah terakhir tee index.html, yang membuka file untuk ditulis dan oleh karena itu mengosongkannya, berjalan setelah pipa upstream selesai dan output berada di buffer dalam pipa.

Kemungkinan besar yang berikut ini tidak akan berfungsi:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee index.html

- itu akan menjalankan kedua perintah pipa pada saat yang sama tanpa ada pemblokiran. (Tanpa menghalangi pipa harus melewati garis byte dengan baris bukan buffer oleh penyangga. Sama seperti ketika Anda menjalankan cat | sed s/bar/GGG/. Tanpa menghalangi itu lebih interaktif dan biasanya pipa dari hanya 2 perintah berjalan tanpa buffering dan menghalangi. Pipa yang lebih panjang akan buffered.) The tee index.htmlkehendak buka file untuk ditulis dan itu akan dikosongkan. Namun, jika Anda selalu mengaktifkan buffering, versi kedua juga akan berfungsi.

xealits
sumber
3
File output tee juga dibuka segera menghasilkan index.html kosong untuk seluruh perintah.
sjngm
3
Ini akan merusak file input apa pun yang lebih besar dari buffer pipa (yang biasanya 64KB) . (@sjngm: file tidak terpotong secara instan >, tetapi intinya adalah bahwa ini adalah solusi rusak yang kemungkinan menyebabkan hilangnya data).
mklement0
4

Masalah dengan perintah

sed 'code' file > file

adalah yang filedipotong oleh shell sebelum sed benar-benar bisa memprosesnya. Akibatnya, Anda mendapatkan file kosong.

Cara sed untuk melakukan ini adalah menggunakan -iuntuk mengedit di tempat, seperti yang disarankan jawaban lain. Namun, ini tidak selalu seperti yang Anda inginkan. -iakan membuat file sementara yang kemudian akan digunakan untuk mengganti file asli. Ini bermasalah jika file asli Anda adalah tautan (tautan akan diganti dengan file biasa). Jika Anda perlu menyimpan tautan, Anda dapat menggunakan variabel sementara untuk menyimpan hasil sed sebelum menulis kembali ke file, seperti ini:

tmp=$(sed 'code' file); echo -n "$tmp" > file

Lebih baik lagi, gunakan printfdaripada echokarena echokemungkinan akan diproses \\seperti \pada beberapa shell (misalnya tanda hubung):

tmp=$(sed 'code' file); printf "%s" "$tmp" > file
Andrzej Pronobis
sumber
1
+1 untuk melestarikan tautan. Ini juga berfungsi dengan file sementara:sed 'code' file > file.tmp; cat file.tmp > file; rm file.tmp
dashohoxha
3

Dan edjawabannya:

printf "%s\n" '1,$s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' w q | ed index.html

Untuk mengulangi apa yang dijawab oleh codaddict , shell menangani pengalihan terlebih dahulu , menghapus file "input.html", dan kemudian shell memanggil perintah "sed" meneruskannya dengan file yang sekarang kosong.

glenn jackman
sumber
2
pertanyaan singkat, mengapa orang terus memberikan " edversi" sedjawaban? apakah kinerjanya lebih cepat?
cregox
6
Beberapa sedtidak menerapkan -iuntuk mengedit di tempat. edada di mana-mana dan tidak memungkinkan Anda menyimpan suntingan ke file asli. Plus, selalu baik untuk memiliki banyak alat dalam kit Anda.
glenn jackman
OK keren. jadi, kinerja bijaksana, mereka sama saya kira. Terima kasih!
cregox
2

Anda dapat menggunakan Vim dalam mode Ex:

ex -sc '%s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g|x' index.html
  1. % pilih semua baris

  2. x Simpan dan tutup

Steven Penny
sumber
0

Saya sedang mencari opsi di mana saya dapat menentukan rentang garis dan menemukan jawabannya. Misalnya saya ingin mengganti host1 ke host2 dari baris 36-57.

sed '36,57 s/host1/host2/g' myfile.txt > myfile1.txt

Anda dapat menggunakan opsi gi juga untuk mengabaikan huruf besar-kecil.

sed '30,40 s/version/story/gi' myfile.txt > myfile1.txt

sumber
0

Dengan segala hormat terhadap jawaban yang benar di atas, itu selalu merupakan ide yang baik untuk "keringkan" skrip seperti itu, sehingga Anda tidak merusak file Anda dan harus memulai lagi dari awal.

Dapatkan saja skrip Anda untuk menumpahkan output ke baris perintah alih-alih menulisnya ke file, misalnya, seperti itu:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

ATAU

less index.html | sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g 

Dengan cara ini Anda dapat melihat dan memeriksa output dari perintah tanpa file Anda terpotong.

Nestor Milyaev
sumber