Як робиться вбудована модифікація файлу?


10

Що означає модифікація файлу "inplace", наприклад, через sed -iабо perl -iозначає?
Моє запитання про те, як робиться ця модифікація місця. Чи скопійований файл модифікація робиться в копії, а потім замінюється оригінал? Або оригінальний файл якимось чином змінюється на місці?


Подивіться на backreference.org/2011/01/29/in-place-editing-of-files для детального пояснення цієї теми.
scy

З цього питання, як це робиться з exабо vi?
Wildcard

@Wildcard - кожен з них має цілу систему. exпідтримує поштовий файл (наприклад, dead.mailабо щось у вас, і інше, десь поруч із вашим поштовим спілером, як правило) . перевірте характеристики - кожен з них має стан, визначений на велику довжину ... exмає власний двійковий формат у більшості випадків (подивіться на ваш -rescueфайл), і це використовується для попереднього розділення тимчасових буферних файлів (можливо, до шести) . тож ці копіюють блоки введення для редагування буферів та синхронізації записує в зсуви за зміни :!written?
mikeserv

Відповіді:


18

sed створює тимчасовий файл, записує вихід у цей файл, а потім перейменовує тимчасовий файл у верхній частині оригіналу.

Ви можете подивитися, що відбувається за допомогою 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 +++

Це записує всі файлові операції sed: він створює новий файл (безпечно з O_CREAT|O_EXCL), записує в нього дані, а потім переміщує їх у верхній частині мого оригінального файлу a.

sed -iприймає суфікс для використання для резервного копіювання, і в цьому випадку він спочатку переміщує оригінал (а не перейменування на верхній). Цей аргумент є обов'язковим у більшості BSD sed. У цьому випадку короткий час, коли в каталозі взагалі немає файлу з правильним іменем.

perl в останніх версіях відкривається вхідний файл, потім видаляється і створюється новий файл з тим же ім’ям:

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

Коли ви видаляєте ( unlink) файл, який ви вже відкрили, ви зберігаєте доступ до нього так довго, поки ви не тримаєте ручку, щоб він міг читати дані зі видаленого файлу. Таким чином perlзаписується безпосередньо у вихідний файл, а не у тимчасовий файл: додатковий файл не створюється, але якщо ви прочитаєте файл під час процесу, ви отримаєте частковий вміст, на відміну від sedпідходу 's. Існує також короткий час, коли немає файлу з правильним іменем, який знаходиться на початку процесу, а не в кінці (як в sed -i .bak).


І те, sedі perlбуде:

  • Замініть символічне посилання звичайним файлом.
  • Розривайте жорсткі зв’язки.
  • Зберігайте право власності на групу, якщо можливо.
  • Створіть файл зі своєю групою за замовчуванням (або групою батьківського каталогу, якщо у цьому каталозі є setgidбіт), якщо ним належить група, у якій ви не входите, і ви не root.
  • Збережіть право власності на файл, якщо ви root.
  • Зберегти основні дозволи.
  • Збережіть setuidі setgrpбіти, якщо отримана група збігається з групою, в якій вона почалася.
  • Збережіть липкий шматочок.
  • Не зберегти xattrs.

sed буде:

  • Збережіть ACL (у Linux; про інших я не знаю) .

perl буде:

  • Не зберігати ACL.

Сказане вище стосується Linux з GNU sedта Mac OS X з його (похідним FreeBSD) sed.


3

Додатково до відповіді @ Гомера від perldoc perlrun:

визначає, що файли, оброблені конструкцією "<>", слід редагувати на місці. Це робиться шляхом перейменування вхідного файлу, відкриття вихідного файла на оригінальне ім'я та вибору цього вихідного файла як типового для операторів print (). Розширення, якщо воно постачається, використовується для зміни імені старого файлу для створення резервної копії, дотримуючись цих правил:

Якщо розширення не надається, резервне копіювання не робиться і поточний файл перезаписується.

Якщо розширення не містить *, воно додається до кінця поточного імені файлу як суфікс. Якщо розширення містить один або більше * символів, то кожен * замінюється поточним іменем файлу.

І пам’ятайте, що жодне м'яке або жорстке посилання не зберігається:

Зауважте, що оскільки -i перейменовує або видаляє початковий файл перед створенням нового файлу з тим самим іменем, м'які та жорсткі посилання в стилі UNIX не зберігатимуться.

Нарешті, перемикач -i не перешкоджає виконанню, коли в командному рядку не вказано жодних файлів. У цьому випадку не створюється резервне копіювання (оригінальний файл, звичайно, не може бути визначений), і обробка переходить від STDIN до STDOUT, як можна було очікувати.

Це також пояснює , чому ви повинні використовувати -iз -pопцією або використовувати явне printзаяву , якщо ви хочете редагувати INPLACE з 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.