cp se comporte bizarrement quand. (point) ou .. (point point) sont le répertoire source

15

Cette réponse révèle que l'on peut copier tous les fichiers - y compris ceux cachés - d'un répertoire srcà l'autre destcomme ceci:

mkdir dest
cp -r src/. dest

Il n'y a aucune explication dans la réponse ou ses commentaires pour expliquer pourquoi cela fonctionne réellement, et personne ne semble trouver non plus de documentation à ce sujet.

J'ai essayé quelques trucs. Tout d'abord, le cas 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

Puis, avec /.à la fin:

$ 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

Ainsi, cela se comporte de la même manière *, mais copie également les fichiers cachés.

$ 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

.et ..sont des liens physiques appropriés, comme expliqué ici , tout comme l'entrée de répertoire elle-même.

D'où vient ce comportement et où est-il documenté?

iFreilicht
la source
3
Que voulez-vous dire que personne ne peut trouver de documentation? La cpréférence explique clairement comment cp -Rfonctionne. .et ..sont des répertoires comme tous les autres répertoires, ils n'ont rien de magique ou de mystérieux.
AlexP
2
@AlexP J'ai édité la réponse pour la rendre plus claire. Le fait est que , .et ..ne se comportent pas comme les autres répertoires.
iFreilicht
J'ai essayé d'expliquer pourquoi cela fonctionne à Comment copier un dossier de manière récursive de manière idempotente en utilisant cp
roaima

Réponses:

27

Le comportement est un résultat logique de l'algorithme documenté pour cp -R. Voir POSIX , étape 2f:

Les fichiers du répertoire source_file doivent être copiés dans le répertoire dest_file , en suivant les quatre étapes (1 à 4) répertoriées ici avec les fichiers en tant que source_files .

.et ..sont des répertoires, respectivement le répertoire actuel et le répertoire parent. Aucun n'est spécial en ce qui concerne le shell, donc aucun n'est concerné par l'expansion, et le répertoire sera copié, y compris les fichiers cachés. *, d'autre part, sera étendu à une liste de fichiers, et c'est là que les fichiers cachés sont filtrés.

src/.est le répertoire courant à l'intérieur src, qui est srclui - même; src/src_dir/..est src_dirle répertoire parent de, qui l'est encore src. Donc de l'extérieur src, si srcest un répertoire, spécifiant src/.ou src/src_dir/..comme fichier source pour cpsont équivalents, et copiez le contenu de src, y compris les fichiers cachés.

Le point de préciser src/.est qu'il échouera s'il srcne s'agit pas d'un répertoire (ou d'un lien symbolique vers un répertoire), alors srcqu'il ne le serait pas. Il copiera également le contenu de srcseulement, sans se copier src; cela correspond aussi à la documentation:

Si la cible existe et nomme un répertoire existant, le nom du chemin de destination correspondant pour chaque fichier dans la hiérarchie de fichiers doit être la concaténation de la cible , un seul caractère de barre oblique si la cible ne s'est pas terminée par une barre oblique et le chemin d'accès du fichier relatif dans le répertoire contenant le fichier source .

Copie donc cp -R src/. destle contenu de srcdans dest/.(le fichier source est .dedans src), tandis que cp -R src destcopie le contenu de srcdans dest/src(le fichier source est src).

Une autre façon de penser est de comparer la copie src/src_diret src/., plutôt que de comparer src/.et src. .se comporte comme src_dirdans le premier cas.

Stephen Kitt
la source
Mais cela ne se comporte pas de la même manière. Spécifier srccopiera le répertoire dans dest, src/.copiera le contenu. Je vais essayer de clarifier cela dans la question.
iFreilicht
Là, je pense que cela répond à votre question sous-jacente.
Stephen Kitt
1
@ Stéphane l'OP compare la copie src/.et src/*(notez pas src/.* ); src/*n'inclut pas les fichiers cachés si la globalisation les ignore ...
Stephen Kitt
1
Hmm, "répertoire contenant le fichier source ". Eh bien, srccontient évidemment, src/.mais cela signifie que le répertoire contenant un répertoire dépend de la façon dont vous nommez le répertoire. Bien sûr, l'existence des .liens signifie que tous les répertoires se contiennent, mais cela peut ne pas être intuitif pour tous. Au lieu de ce comportement, on pourrait également être tenté de supposer que "le répertoire contenant le répertoire foo" serait déterminé par foo/.., auquel cas cela n'aurait pas d'importance si nous nous référions à fooou foo/.: le répertoire contenant résultant serait le même.
ilkkachu
1
C'est-à-dire que la distinction entre fooet foo/.semble un peu délicate, mais ça ne me dérange pas, je trouve aussi ça un peu amusant.
ilkkachu
1

Lorsque vous courrez cp -R src/foo dest, vous obtiendrez dest/foo. Donc, si le répertoire dest/foon'existe pas, cple créera, puis copiera le contenu de src/foodans dest/foo.

Lorsque vous exécutez cp -R src/. dest, cpvoit que dest/.existe, et il est juste la question de copier le contenu src/.à dest/..

Lorsque vous le considérez comme la copie d'un répertoire nommé à .partir de srcet la fusion de son contenu avec le répertoire existant dest/., cela aura du sens.

telcoM
la source