Как выполняется модификация файла на месте?

10

Что означает изменение файла на месте, например, через 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подхода, используемого в этом подходе. Также существует короткое время, когда нет файла с правильным именем, который находится в начале процесса, а не в конце (как в 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выражение, если вы хотите редактировать на месте с помощью 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
источник