cp se comporta estranhamente quando. (ponto) ou .. (ponto) são o diretório de origem

15

Esta resposta revela que é possível copiar todos os arquivos - incluindo os ocultos - do diretório srcpara o diretório da seguinte destmaneira:

mkdir dest
cp -r src/. dest

Não há explicação na resposta ou em seus comentários sobre por que isso realmente funciona e ninguém parece encontrar documentação sobre isso também.

Eu tentei algumas coisas. Primeiro, o caso normal:

$ 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

Então, com /.no final:

$ 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

Portanto, isso se comporta de maneira semelhante a *, mas também copia arquivos ocultos.

$ 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

.e ..são links físicos adequados, conforme explicado aqui , assim como a própria entrada do diretório.

De onde vem esse comportamento e onde está documentado?

iFreilicht
fonte
3
Como assim, ninguém pode encontrar documentação? A cpreferência explica claramente como cp -Rfunciona. .e ..são diretórios como qualquer outro diretório, não há nada de mágico ou misterioso neles.
AlexP # 6/17
2
@AlexP Eu editei a resposta para torná-la mais clara. O ponto principal é isso .e ..não se comporta como outros diretórios.
iFreilicht

Respostas:

27

O comportamento é um resultado lógico do algoritmo documentado para cp -R. Consulte POSIX , etapa 2f:

Os arquivos no diretório source_file devem ser copiados para o diretório dest_file , executando as quatro etapas (1 a 4) listadas aqui com os arquivos como source_files .

.e ..são diretórios, respectivamente, o diretório atual e o diretório pai. Nenhum dos dois é especial no que diz respeito ao shell, portanto, nem à expansão, e o diretório será copiado, incluindo arquivos ocultos. *, por outro lado, será expandido para uma lista de arquivos e é aqui que os arquivos ocultos são filtrados.

src/.é o diretório atual dentro src, que é srcele próprio; src/src_dir/..é src_diro diretório pai, que é novamente src. Portanto, de fora src, se srcfor um diretório, especificar src/.ou src/src_dir/..como o arquivo de origem cpé equivalente e copiar o conteúdo de src, incluindo arquivos ocultos.

O ponto de especificar src/.é que ele falhará se srcnão for um diretório (ou link simbólico para um diretório), enquanto srcnão. Ele também copiará o conteúdo de srconly, sem se copiar src; isso também corresponde à documentação:

Se o destino existir e nomear um diretório existente, o nome do caminho de destino correspondente para cada arquivo na hierarquia de arquivos deve ser a concatenação do destino , um único caractere de barra se o destino não terminar em uma barra e o nome do caminho do arquivo relativo para o diretório que contém o arquivo de origem .

Então, cp -R src/. destcopia o conteúdo de srcpara dest/.(o arquivo de origem está .dentro src), enquanto cp -R src destcopia o conteúdo de srcpara dest/src(o arquivo de origem está src).

Outra maneira de pensar nisso é comparar cópia src/src_dire src/., em vez de comparar src/.e src. .comporta-se como src_dirno caso anterior.

Stephen Kitt
fonte
Mas não se comporta da mesma maneira. A especificação srccopiará o diretório para dest, src/.copiará o conteúdo. Vou tentar deixar isso mais claro na pergunta.
iFreilicht
Acho que isso responde à sua pergunta subjacente.
Stephen Kitt
1
@ Stéphane, o OP compara cópia src/.e src/*(observe, não src/.* ); src/*não inclui arquivos ocultos se ignora englobamento eles ...
Stephen Kitt
1
Hmm, "diretório que contém o arquivo de origem ". Bem, obviamente srccontém, src/.mas significa que o diretório que contém um diretório depende de como você o nomeia. Obviamente, a existência dos .links significa que todos os diretórios se contêm, mas isso pode não ser intuitivo para todos. Em vez desse comportamento, pode-se também tentar assumir que "o diretório que contém o diretório foo" seria determinado por foo/.., caso em que não importaria se nos referíssemos a : fooou foo/.: o diretório contendo resultante seria o mesmo.
ilkkachu
1
O que quer dizer que a distinção entre fooe foo/.parece um pouco delicada, mas não me importo, também acho um pouco divertido.
Ilkkachu
1

Quando você corre cp -R src/foo dest, você recebe dest/foo. Portanto, se o diretório dest/foonão existir, cpcrie-o e copie o conteúdo de src/foopara dest/foo.

Quando você executa cp -R src/. dest, cpvê que dest/.existe, e então é apenas a questão de copiar o conteúdo de src/.para dest/..

Quando você pensa nisso como copiar um diretório chamado .de srce mesclando seu conteúdo com o diretório existente dest/., ele vai fazer sentido.

telcoM
fonte