Почему «cp -ra/. b» ведет себя именно так?

Почему «cp -ra/. b» ведет себя именно так?

Я думал, что aи a/.это тот же путь. Однако я обнаружил, чтосриrsyncкопировать содержимое каталога вместо самого каталога при /.добавлении в исходный путь. Я также пробовал a/inner/..; это тоже сработало.

$ cp -r a b  # Copies dir a into dir b.
$ cp -r a/. b  # Copies files from dir a into dir b.
$ cp -r a/inner/.. b  # Also copies files from dir a into dir b.
$ cd a && cp -r . ../b  # One more way to copy inner files.

Я понимаю, что это полезно. Но я немного сбит с толку, потому что, похоже, эта функция нарушает стандарты.

Как это работает? Эта функция где-то задокументирована? Это функция ОС,срили баш?

решение1

Я понимаю, что это полезно. Но я немного сбит с толку, потому что, похоже, эта функция нарушает стандарты.

срповедение при рекурсивном копировании из a/.в bполностью соответствует его «нормальному» поведению.

По умолчанию,ср's не создает родительские каталоги. Это можно изменить с помощьюродителивыключатель:

   --parents
          use full source file name under DIRECTORY

Но что это значит?

Это означает, что пока команда

cp --parents -r some/path/to/source dest

скопирует содержимое исходного каталога в dest/some/path/to/source, команда

cp -r some/path/to/source dest

скопирует содержимое исходного каталога в dest/source.

Аналогично, команда

cp -r some/path/to/source/. dest

скопирует содержимое исходного каталога в dest/., который представляет собой просто dest.

Я думал, aи a/.это тот же путь.

aиa/. являетсятот же путь. Но как аргумент кср, это просто строка.

Обратите внимание, что команды

cp --parents -r some/path/to/source dest

и

cd some/path/to && cp --parents -r source dest

также будет вести себя по-другому.


А как насчет cp -r a/inner/.. b? Учитывая ваше объяснение, разве не должно оно копировать файлы в b/..(т.е. в текущий каталог)?

Ну да. Это исключение.

По крайней мере в версии GNUср, существует особый случай для ..базового имени.

Отcoreutils-8.22/src/cp.c:

          if (parents_option)
            {
              [removed]
            }
          else
            {
              char *arg_base;
              /* Append the last component of 'arg' to 'target_directory'.  */

              ASSIGN_BASENAME_STRDUPA (arg_base, arg);
              /* For 'cp -R source/.. dest', don't copy into 'dest/..'. */
              dst_name = (STREQ (arg_base, "..")
                          ? xstrdup (target_directory)
                          : file_name_concat (target_directory, arg_base,
                                              NULL));
            }

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

В конце концов, я не думаю, что кто-то ожидал бы команды

cp -r .. ~

чтобы повлиять на файлы за пределами его домашнего каталога...

решение2

$ mkdir a b a/inner
$ touch a/a{1..3} b/b{1..3}
$ ls -R
.:
a  b

./a:
a1  a2  a3  inner

./a/inner:

./b:
b1  b2  b3
$ cp a b
cp: omitting directory ‘a’
$ cp a/. b
cp: omitting directory ‘a/.’
$ cp a/inner/.. b
cp: omitting directory ‘a/inner/..’
$ cd a && cp . ../b
cp: omitting directory ‘.’
$ cd ..
$ ls -R
.:
a  b

./a:
a1  a2  a3  inner

./a/inner:

./b:
b1  b2  b3

Ничего из того, что вы говорите, не происходит, на самом деле не происходит. Четыре cpкоманды ничего не делают. Возможно, у вас есть псевдоним для cpзагруженного. Вы можете проверить это с помощью alias cp.

решение3

Кстати rsync(1), прочитайте его руководство.осторожно. Он использует некоторые необычные соглашения для обозначения «содержимого каталога» вместо «каталога и его содержимого».

Для cp(1)и других команд Linux, вядроlevel aи a/.и даже a/inner/..ссылаются на один и тот же каталог. Это не значит, что приложение не может разобрать их самостоятельно и придать им странные разные значения, но проще просто отправить строку ядру, которое сделает все правильно.

решение4

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

Связанный контент