Я очень хорошо понимаю понятие жестких ссылок и несколько раз читал справочные страницы по основным инструментам, таким как cp
- и даже последние спецификации POSIX. Тем не менее я был удивлен, наблюдая следующее поведение:
$ echo john > john
$ cp -l john paul
$ echo george > george
На данный момент john
и paul
будет иметь одинаковый инод (и контент), и george
будет отличаться в обоих отношениях. Теперь мы делаем:
$ cp george paul
В этот момент я ожидал george
и paul
иметь разные номера индексных дескрипторов , но то же самое содержание --- это ожидание сбылось --- но я также ожидал , paul
чтобы теперь имеют разное количество индексных дескрипторов из john
, и john
до сих пор содержание john
. Это где я был удивлен. Оказывается, что копирование файла в путь назначения paul
также приводит к установке того же файла (того же самого inode) по всем другим путям назначения, которые совместно используют paul
inode. Я думал, что cp
создает новый файл и перемещает его в место, ранее занимаемое старым файлом paul
. Вместо этого он, похоже, открывает существующий файл paul
, обрезает его и пишетgeorge
содержание в этот существующий файл. Следовательно, любые «другие» файлы с таким же индексом обновляются «своим» содержимым одновременно.
Хорошо, это систематическое поведение, и теперь, когда я знаю, что ожидать этого, я могу понять, как обойти это или воспользоваться этим, в зависимости от обстоятельств. Что меня озадачивает, когда я должен был увидеть это поведение задокументированным? Я был бы удивлен, если бы это не было документировано где-то в документах, на которые я уже смотрел. Но, видимо, я пропустил это, и теперь не могу найти источник, который обсуждает это поведение.
cp
документы, которые перезаписывают файл назначения, если файл назначения уже присутствует. Вы правы в том, что в нем не указано подробно, что означает «перезаписать», но определенно говорится «перезаписать», а не «заменить». Если вы хотите быть педантичным, вы можете утверждать, что «перезапись» - это именно то, чтоcp
нужно, и ожидаемое поведение будет правильно называться «заменить».Также обратите внимание, что если
cp
«заменить» уже существующие целевые файлы, это может считаться неожиданным или неправильным, скорее, чем «перезапись». Например:cp
сначала удалить старый файл, а затем создать новый, то будет интервал времени, в течение которого файл будет отсутствовать, что было бы удивительно.cp
сначала создать временный файл, а затем переместить его на место, он, вероятно, должен это задокументировать, поскольку такие временные файлы со странными именами иногда замечаются ... но это не так.cp
не удалось создать новый файл в том же каталоге, что и старый файл из-за разрешений, это было бы неудачно (особенно если он уже удалил старый).cp
пользователю, а запущенныйcp
- нет,root
было бы невозможно сопоставить владельца и разрешения нового файла с правами нового файла.cp
он не знает, то они будут потеряны в копии. В настоящее время реализацииcp
должны надежно понимать такие вещи, как расширенные атрибуты, но это было не всегда так. И есть и другие вещи, такие как вилки ресурсов MacOS или, для удаленных файловых систем, в основном все что угодно.Итак, в заключение: теперь вы знаете, что на
cp
самом деле. Вы никогда не будете удивлены этим снова! Честно говоря, я думаю, что то же самое могло случиться и со мной много лет назад.источник
man
страницы дляcp
версий BSD (по крайней мере, OSX) и Gnucp
не так явно говорят о «перезаписи». Это слово используется только в комментариях к опциям-i
и-n
. Мануальная страница Gnu особенно неинформативна,Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.
In the first synopsis form, the cp utility copies the contents of the source_file to the target_file.
‘cp’ copies files (or, optionally, directories). The copy is completely independent of the original.
Я вижу, что стандарт POSIX 2013 действительно определяет наблюдаемое поведение . Это говорит:
источник
cp
что даст аналогичные результатыmv
и сломает любые жесткие ссылки, частью которых является dest. Но теперь, когда я думаю об этом, это будет означать, что он должен был бы специально предназначатьсяunlink(2)
для target (cp -f
), или создать временное имя с другим именем, а затемrename(2)
это. Простая реализация состоит в том, чтобы просто открыть файл для перезаписи, чего требует POSIX. Это эквивалентноcat src > dest
Если вы можете сказать: «Копирование файла по пути назначения
paul
также копирует тот же файл (тот же самый inode) во все другие пути назначения, которые совместно используютpaul
inode». Мне жаль говорить, что вы не понимаете понятие жесткие ссылки очень хорошо. Если я дам яблоко сэру Маккартни, я дам яблоко Полу, и я дал яблоко партнеру по написанию песен Джона Леннона. Но я не выдал три яблока; Я дал яблоко человеку, у которого есть несколько имен / названий / дескрипторов.Точно так же, когда вы копируете
george
кpaul
, вы не также копировать егоjohn
. Скорее, вы копируетеgeorge
данные в файл, на индекс которого указываетpaul
запись каталога.Шаг за шагом: когда вы делаете
Вы создали новый файл (при условии, что
john
в этом каталоге еще не было файла с именем ). Или, если говорить более строго, это предполагает, что в каталоге уже не было записи каталога с именемjohn
(потому что, строго говоря, в каталогах нет файлов; только записи каталога, которые указывают на inode). После того как выили
вы не создали новый файл; скорее, вы дали существующему файлу новое имя. Теперь у вас есть файл с двумя именами:
john
иpaul
. И когда вы говоритеВы перезаписываете этот файл . Тот факт, что у него есть два имени, не имеет значения; он может иметь 42 имени, возможно, в местах, к которым вы даже не можете получить доступ, и эта команда не будет копировать
george\n
данные во все эти имена (пути); это просто копирование данных в один файл с несколькими именами.источник
john
и яpaul
начинал как два пути к одному и тому же файлу. Но это был самый простой способ выразить себя. Я не думаю, что само понятие жесткой связи, правильно понятое, диктует любое из двух вариантов поведенияcp
(без-l
).