Por que “cp -r a /. b ”se comporta como faz?

4

Eu achei que a e a/. é o mesmo caminho. No entanto, descobri que cp e rsync copiam o conteúdo do diretório em vez do próprio diretório quando /. é adicionado ao caminho de origem. Eu também tentei a/inner/.. ; isso também funcionou.

$ 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.

Eu entendo que isso é útil. Mas estou um pouco confuso porque parece que esse recurso quebra os padrões.

Como isso funciona? Este recurso está documentado em algum lugar? Esta é uma característica do sistema operacional, cp ou bash?

    
por raacer 04.03.2014 / 16:32

4 respostas

1

I understand that this is useful. But I'm a bit confused because it seems this feature breaks standards.

O comportamento

cp ao copiar recursivamente de a/. para b é perfeitamente consistente com seu comportamento "normal".

Por padrão, o cp não cria diretórios pai. Isso pode ser modificado com a opção parents :

   --parents
          use full source file name under DIRECTORY

Mas o que isso significa?

Isso significa que, enquanto o comando

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

copiará o conteúdo do diretório de origem em dest/some/path/to/source , o comando

cp -r some/path/to/source dest

copiará o conteúdo do diretório de origem em dest/source .

Da mesma forma, o comando

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

copiará o conteúdo do diretório de origem em dest/. , que é apenas dest .

I thought a and a/. is the same path.

a e a/. é o mesmo caminho. Mas como um argumento para cp , é apenas uma string.

Note que os comandos

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

e

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

também se comportará de maneira diferente.

What about cp -r a/inner/.. b? Taking into account your explanation, should not it copy files to b/.. (i.e. to the current directory)?

Bem, sim. Esta é uma exceção.

Pelo menos na versão GNU de cp , há um caso especial para o .. basename.

Em 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));
            }

A motivação parece ser evitar a cópia fora da pasta de destino, o que - embora perfeitamente consistente com o comportamento do cp em todos os outros casos - é um pouco contraintuitivo e pode ter consequências desagradáveis.

Afinal, eu não acho que alguém esperaria o comando

cp -r .. ~

para afetar arquivos fora de seu diretório pessoal ...

    
por 04.03.2014 / 22:19
1
$ 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

Nenhuma das coisas que você diz acontecem, realmente acontecem. Os quatro comandos cp não fazem nada. Talvez você tenha um alias para cp loaded. Você pode verificar isso com alias cp .

    
por 04.03.2014 / 19:57
0

Apropos rsync(1) , leia o manual cuidadosamente . Ele usa algumas convenções incomuns para significar "o conteúdo do diretório" versus "o diretório e seu conteúdo".

Para cp(1) e outros comandos do Linux, no nível kernel a e a/. e até a/inner/.. referem-se ao mesmo diretório exato. Isso não significa que o aplicativo não possa analisá-los por si mesmo e dar a eles diferentes significados, mas é mais fácil simplesmente enviar a string para o kernel, que fará a coisa certa.

    
por 04.03.2014 / 21:44
-1

Se bem me lembro, acredito que o. Neste exemplo, funciona de forma semelhante a um *. Em essência, um caractere curinga.

    
por 04.03.2014 / 17:05

Tags