СР ведет себя странно, когда. (точка) или .. (точка) - исходный каталог

15

Этот ответ показывает, что можно скопировать все файлы, включая скрытые, из каталога srcв каталог destследующим образом:

mkdir dest
cp -r src/. dest

В ответе или его комментариях нет объяснения, почему это действительно работает, и, похоже, никто не находит документацию по этому вопросу.

Я опробовал несколько вещей. Во-первых, нормальный случай:

$ mkdir src src/src_dir dest && touch src/src_file src/.dotfile dest/dest_file
$ cp -r src dest
$ ls -A dest
dest_file  src

Затем с /.в конце:

$ mkdir src src/src_dir dest && touch src/src_file src/.dotfile dest/dest_file
$ cp -r src/. dest
$ ls -A dest
dest_file  .dotfile  src_dir  src_file

Таким образом, это ведет себя аналогично *, но также копирует скрытые файлы.

$ mkdir src src/src_dir dest && touch src/src_file src/.dotfile dest/dest_file
$ cp -r src/* dest
$ ls -A dest
dest_file  src_dir  src_file

.и ..являются правильными жесткими ссылками, как объяснено здесь , так же, как и сама запись каталога.

Откуда происходит это поведение и где оно задокументировано?

iFreilicht
источник
3
Что значит никто не может найти документацию? cpСсылка ясно объясняет , как cp -Rработает. .и ..каталоги, как и любые другие каталоги, в них нет ничего волшебного или таинственного.
AlexP
2
@AlexP Я отредактировал ответ, чтобы сделать его более понятным. Все дело в том, что .и ..не ведите себя как другие каталоги.
iFreilicht
Я попытался объяснить, почему это работает в
разделе

Ответы:

27

Поведение является логическим результатом документированного алгоритма для cp -R. Смотрите POSIX , шаг 2f:

Файлы в каталоге source_file должны быть скопированы в каталог dest_file , выполнив четыре шага (от 1 до 4), перечисленных здесь, с файлами как source_files .

.и ..каталоги, соответственно текущий каталог и родительский каталог. Ни то, ни другое не является особенным с точки зрения оболочки, поэтому ни то, ни другое не касается расширения, и каталог будет скопирован, включая скрытые файлы. *с другой стороны, будет расширен список файлов, и именно здесь скрытые файлы отфильтрованы.

src/.текущий каталог внутри src, который srcсам по себе; src/src_dir/..Это src_dirродительский каталог, который снова src. Поэтому извне src, если srcэто каталог, указывающий src/.или src/src_dir/..в качестве исходного файла cpэквивалентный, и скопировать содержимое src, включая скрытые файлы.

Смысл определения в src/.том, что он потерпит неудачу, если srcне является каталогом (или символической ссылкой на каталог), тогда как srcне будет. Он также будет копировать только содержимое src, не копируя srcсебя; это также соответствует документации:

Если цель существует и присваивает имя существующему каталогу, именем соответствующего пути назначения для каждого файла в файловой иерархии должна быть конкатенация цели , одиночный символ косой черты, если цель не заканчивается косой чертой, и путь к файлу относительно в каталог, содержащий исходный_файл .

Таким образом cp -R src/. destкопирует содержимое srcв dest/.(исходный файл находится .в src), тогда как cp -R src destкопирует содержимое srcв dest/src(исходный файл src).

Еще один способ думать об этом - сравнивать копирование src/src_dirи src/., а не сравнивать src/.и src. .ведет себя так же, как src_dirв первом случае.

Стивен Китт
источник
Но это не так. Указав src, скопирует каталог в dest, src/.скопирует содержимое. Я постараюсь прояснить этот вопрос.
iFreilicht
Там, я думаю, это отвечает на ваш основной вопрос.
Стивен Китт
1
@ Стефан ОП сравнивает копирование src/.и src/*(заметьте, нет src/.* ); src/*не включает скрытые файлы, если их игнорирует
Стивен Китт
1
Хм, "каталог, содержащий исходный_файл ". Ну, очевидно, srcсодержит, src/.но это означает, что содержащий каталог каталога зависит от того, как вы называете каталог. Конечно, наличие .ссылок в некотором смысле означает, что все каталоги содержат самих себя, но это может быть не интуитивно понятно для всех. Вместо этого поведения можно было бы также предположить, что «каталог, содержащий каталог foo» будет определяться foo/.., и в этом случае не будет иметь значения, будем ли мы ссылаться на fooили foo/.: результирующий содержащий каталог будет таким же.
ilkkachu
1
То есть различие между fooи foo/.кажется немного деликатным, но я не против, я также нахожу это немного забавным.
ilkkachu
1

Когда вы бежите cp -R src/foo dest, вы получите dest/foo. Таким образом, если каталог dest/fooне существует, cpсоздайте его, а затем скопируйте содержимое src/fooв dest/foo.

Когда вы запускаете cp -R src/. dest, видите , cpчто dest/.существует, и тогда это просто вопрос копирования содержимого src/.в dest/..

Когда вы думаете о нем , как копирование каталога с именем .из srcи объединения его содержимое существующего каталога dest/., это будет иметь смысл.

Телком
источник