Bagaimana modifikasi inplace dari file dilakukan?

10

Apa yang dimaksud dengan "inplace" modifikasi file misalnya via sed -iatau perl -iartinya?
Pertanyaan saya adalah tentang bagaimana modifikasi inplace ini dilakukan. Apakah file yang disalin modifikasi dilakukan dalam salinan dan kemudian ganti yang asli? Atau apakah file asli entah bagaimana sedang dimodifikasi di tempat?

Jim
sumber
Lihat backreference.org/2011/01/29/in-place-editing-of-files untuk penjelasan terperinci tentang topik ini.
scy
Dalam hal ini, bagaimana ini dilakukan dengan exatau vi?
Wildcard
@Wildcard - masing-masing memiliki seluruh sistem di tempatnya. exmemelihara filefile (seperti, dead.mailatau sesuatu di ~ Anda, dan tempat lain yang dekat dengan spooler email Anda, biasanya) . periksa spesifikasi - masing-masing dari mereka memiliki status yang didefinisikan dengan panjang lebar ... exmemiliki format biner sendiri dalam banyak kasus (lihat -rescuefile Anda ) dan ini digunakan untuk prezero file buffer sementara yang terpisah (mungkin sebanyak enam) . jadi ini menyalin blok input untuk mengedit buffer dan menyinkronkan penulisan ke dalam offset per perubahan :!written?
mikeserv

Jawaban:

18

sed membuat file sementara, menulis output ke file itu, dan kemudian mengganti nama file sementara di atas yang asli.

Anda dapat menonton apa yang terjadi menggunakan strace:

$ strace -e trace=file sed -i -e '' a
execve("/usr/bin/sed", ["sed", "-i", "-e", "", "a"], [/* 34 vars */]) = 0
<...trimmed...>
open("a", O_RDONLY)                     = 3
open("./sedxvhRY8", O_RDWR|O_CREAT|O_EXCL, 0600) = 4
rename("./sedxvhRY8", "a")              = 0
+++ exited with 0 +++

Ini membuat log semua operasi file sed: itu membuat file baru (aman dengan O_CREAT|O_EXCL), menulis data ke dalamnya, dan kemudian memindahkannya kembali ke atas file asli saya a.

sed -imenerima sufiks yang akan digunakan untuk cadangan, dan dalam kasus itu ia memindahkan yang asli terlebih dahulu (alih-alih mengganti nama di atas). Argumen itu wajib di sebagian besar BSD sed. Dalam hal ini, ada waktu singkat ketika tidak ada file dengan nama yang tepat di direktori sama sekali.

perl di versi terbaru buka file input, lalu hapus dan buat file baru dengan nama yang sama:

open("a", O_RDONLY)               = 3
unlink("a")                       = 0
open("a", O_WRONLY|O_CREAT|O_EXCL, 0600) = 4

Ketika Anda menghapus ( unlink) file yang sudah Anda buka, Anda tetap memiliki akses ke sana selama Anda tetap menangani, sehingga bisa terus membaca data dari file yang dihapus. Dengan cara ini perlmenulis langsung ke file output, daripada menjadi file sementara: tidak ada file tambahan yang dibuat, tetapi jika Anda membaca file selama proses Anda akan mendapatkan konten parsial, tidak seperti dengan sedpendekatan. Ada juga waktu singkat ketika tidak ada file dengan nama yang tepat, yang pada awal proses dan bukan pada akhir (seperti dalam sed -i .bak).


Keduanya seddan perlakan:

  • Ganti tautan simbolik dengan file biasa.
  • Hancurkan tautan keras.
  • Pertahankan kepemilikan grup jika memungkinkan.
  • Buat file dengan grup default Anda (atau grup direktori induk jika direktori tersebut memiliki setgidbit) jika file itu dimiliki oleh grup tempat Anda tidak berada dan Anda tidak root.
  • Pertahankan kepemilikan file jika Anda melakukan root.
  • Pertahankan izin dasar.
  • Pertahankan setuiddan setgrpbit, jika grup yang dihasilkan sama dengan grup itu dimulai.
  • Pertahankan bagian yang lengket.
  • Tidak memelihara xattrs.

sed akan:

  • Pertahankan ACL (Di Linux; Saya tidak tahu tentang orang lain) .

perl akan:

  • Tidak memelihara ACL.

Hal di atas berlaku di Linux dengan GNU seddan Mac OS X dengan (FreeBSD-turunannya) sed.

Michael Homer
sumber
3

Sebagai tambahan dari jawaban @ Homer, dari perldoc perlrun:

menetapkan bahwa file yang diproses oleh konstruk "<>" harus diedit di tempat. Ini dilakukan dengan mengganti nama file input, membuka file output dengan nama asli, dan memilih file output sebagai default untuk pernyataan print (). Ekstensi, jika disediakan, digunakan untuk mengubah nama file lama untuk membuat salinan cadangan, mengikuti aturan berikut:

Jika tidak ada ekstensi yang disediakan, tidak ada cadangan yang dibuat dan file saat ini ditimpa.

Jika ekstensi tidak mengandung *, maka ditambahkan ke akhir nama file saat ini sebagai akhiran. Jika ekstensi memang mengandung satu atau lebih * karakter, maka masing-masing * diganti dengan nama file saat ini.

Dan ingat bahwa, tidak ada tautan lunak atau tautan keras yang dipertahankan:

Perhatikan bahwa karena -i mengganti nama atau menghapus file asli sebelum membuat file baru dengan nama yang sama, tautan lunak dan keras gaya UNIX tidak akan dipertahankan.

Akhirnya, -i switch tidak menghalangi eksekusi ketika tidak ada file yang diberikan pada baris perintah. Dalam hal ini, tidak ada cadangan yang dibuat (file asli tidak dapat, tentu saja, ditentukan) dan memproses dari STDIN ke STDOUT seperti yang diharapkan.

Ini juga menjelaskan mengapa Anda harus menggunakan -idengan -ppilihan, atau menggunakan eksplisit printpernyataan jika Anda ingin mengedit inplace dengan perl:

# Opps, file will be truncated, becomes empty
$ perl -i.bak -ne 's/123/qwe/' file

# Right way
$ perl -i.bak -ne 's/123/qwe/;print' file

# Or
$ perl -i.bak -pe 's/123/qwe/' file
cuonglm
sumber