Comment forcer 'cp' à écraser le répertoire au lieu d'en créer un autre à l'intérieur?

107

J'essaye d'écrire un script Bash qui écrasera un répertoire existant. J'ai un répertoire foo/et j'essaye de l'écraser bar/. Mais quand je fais ça:

cp -Rf foo/ bar/

un nouveau bar/foo/répertoire est créé. Je ne veux pas de ça. Il y a deux fichiers dans foo/; aet b. Il existe également des fichiers avec les mêmes noms bar/. Je veux que foo/aet foo/bremplace bar/aet bar/b.

saketrp
la source

Réponses:

122

Vous pouvez le faire en utilisant l' -Toption dans cp.
Voir la page Man pour cp.

-T, --no-target-directory
    treat DEST as a normal file

Donc, selon votre exemple, voici la structure des fichiers.

$ tree test
test
|-- bar
|   |-- a
|   `-- b
`-- foo
    |-- a
    `-- b
2 directories, 4 files

Vous pouvez voir la différence claire lorsque vous utilisez -vpour Verbose.
Lorsque vous utilisez uniquement l' -Roption.

$ cp -Rv foo/ bar/
`foo/' -> `bar/foo'
`foo/b' -> `bar/foo/b'
`foo/a' -> `bar/foo/a'
 $ tree
 |-- bar
 |   |-- a
 |   |-- b
 |   `-- foo
 |       |-- a
 |       `-- b
 `-- foo
     |-- a
     `-- b
3 directories, 6 files

Lorsque vous utilisez l'option, -Til écrase le contenu, traitant la destination comme un fichier normal et non comme un répertoire .

$ cp -TRv foo/ bar/
`foo/b' -> `bar/b'
`foo/a' -> `bar/a'

$ tree
|-- bar
|   |-- a
|   `-- b
`-- foo
    |-- a
    `-- b
2 directories, 4 files

Cela devrait résoudre votre problème.

Saurabh Meshram
la source
22
juste au cas où quelqu'un serait trébuché par cela, cela ne fonctionnera pas avec OSX cp developer.apple.com/library/mac/documentation/Darwin/Reference/…
dnfehren
9
Il n'est pas clair que cette réponse soit ce que l'OP recherche, bien que les exemples donnés ci-dessus masquent le problème ... Avec l'option -T, les fichiers qui sont dans une cible existante ( bar/) mais pas dans la source ( foo/) seront laissés en place, ce n'est donc pas ce que la plupart des gens considèrent comme un écrasement complet du répertoire. c'est à dire. s'il bar/bazexistait déjà, il existerait toujours par la suite ...
robo
1
Cette réponse répond à la question op, mais n'aborde pas le cas où la destination existe déjà et que vous souhaitez supprimer le contenu qu'elle contient, mais pas le répertoire source. Ce n'est pas un comportement attendu de la copie de fichiers d'un endroit à l'autre. Il écrase uniquement dans la cible les éléments qui sont également dans la source, il ne touche à rien dans la cible qui n'est pas dans la source. Vous pouvez nettoyer le dossier cible en ajoutant une commande pour le faire:rm -rf bar/* && cp -TRv foo/ bar/
theferrit32
1
Je ne suis pas un lecteur d'esprit ... Je ne vois pas de clarification supplémentaire sur ce que le PO recherchait, mais c'était DEFINITIVEMENT la réponse que je (MEEEE) recherchait
Assimilateur
2
juste au cas où quelqu'un serait trébuché par la raison pour laquelle le lien dans le commentaire le plus voté ne fonctionne pas, le voici: web.archive.org/web/20170909193852/https://developer.apple.com/…
Sulphur
48

Faites-le en deux étapes.

rm -r bar/
cp -r foo/ bar/
Jonathan Wheeler
la source
11
C'est en fait le seul exemple donné à ce jour qui garantira que le barcontenu est identique à foo, et non une combinaison d'éléments de fooplus d'autres éléments qui ont peut-être déjà existé dans bar. La réponse hautement votée de @Saurabh Meshram ci-dessous a ce problème.
robo
1
Soyez très prudent en utilisant cela, car il va supprimer tous les fichiers de la barre, les même cachés.
Elia Grady
mac osx: rm -rf bar/; cp -r foo/ !$
Michael Dimmitt
1
Ce n'est pas toujours viable ... ou souhaité ... imaginez si fooet barsont de grands répertoires ... peut-être qu'il y a des fichiers barqui ne sont pas dans fooque vous ne voulez pas supprimer. Cela ne devrait pas être considéré comme une réponse réaliste à mon humble avis
Assimilater
Bien que cela a fonctionné pour moi, mais pas la meilleure solution car il peut y avoir un fichier inclus dans foo / mais pas dans bar / qui sera supprimé après cela.
Nitwit
46

Si vous voulez vous assurer que les bar/finitions sont identiques à foo/, utilisez rsyncplutôt:

rsync -a --delete foo/ bar/

Si quelques petites choses ont changé, cela s'exécutera beaucoup plus rapidement que de supprimer et de recopier tout le répertoire.

  • -aest « mode archives », qui copie les fichiers fidèlement foo/auxbar/
  • --deletesupprime les fichiers supplémentaires non foo/de bar/plus, veiller àbar/ finit identiques
  • Si vous voulez voir ce qu'il fait, ajoutez -vh pour verbeux et lisible par l'homme
  • Remarque: la barre oblique après fooest obligatoire, sinon rsynccopiera foo/vers bar/foo/plutôt que l'écrasementbar/ lui - même.
    • (Les barres obliques après les répertoires dans rsync sont déroutantes; si vous êtes intéressé, voici le scoop. Elles indiquent à rsync de faire référence au contenu du répertoire, plutôt qu'au répertoire lui-même . Donc, pour remplacer le contenu de foo/sur le contenu de bar/, nous utilisez une barre oblique sur les deux. C'est déroutant car cela ne fonctionnera pas comme prévu avec une barre oblique sur ni l' un ni l'autre , cependant; rsync interprète toujours sournoisement le chemin de destination comme s'il avait une barre oblique, même s'il honore l'absence de barre oblique sur la source Nous avons donc besoin d'une barre oblique sur le chemin source pour qu'il corresponde à la barre oblique ajoutée automatiquement sur le chemin de destination, si nous voulons copier le contenu de foo/into bar/, plutôt que le répertoirefoo/lui-même atterrissant dans bar/as bar/foo.)

rsync est très puissant et utile, si vous êtes curieux de voir ce qu'il peut faire d'autre (comme copier sur ssh).

Tom Potter
la source
1
J'aime cette réponse mieux que l' cp -Toption car elle fonctionne également sur macosx 👍
tongueroo
18

Utilisez cette cpcommande:

cp -Rf foo/* bar/
anubhava
la source
9
Cela ne supprime pas les fichiers qui sont présents dans bar mais pas dans foo.
Ara le
5
Je ne sais pas ce que tu veux dire par là. Pourquoi la cpcommande devrait- elle supprimer le fichier de la source?
anubhava
Ma compréhension était que lorsque vous «écrasez un répertoire existant» par un autre, à la fin, le répertoire écrasé devrait être une copie de l'autre. C'est-à-dire que la barre de fin devrait être une copie de foo, comme c'est le cas avec @ jonathan-wheeler answer, mais si vous aviez un fichier bar / c et pas de foo / c, bar / c n'est pas supprimé. En passant, je viens de remarquer que ce n'est pas non plus le cas de la réponse de Saurabh Meshram.
Ara
Peut-être que nous ne sommes pas d'accord sur ce que signifie écraser, pour moi c'est essentiellement remplacer, alors que vous pourriez vouloir dire megre? Il est difficile de dire exactement ce que veut OP car elle n'a mentionné que 2 fichiers qui sont à la fois dans foo et bar.
Ara
Cette syntaxe ignore également les fichiers de points cachés. Un grand nombre peut également faire exploser le globe.
xpusostomos
13

La commande suivante garantit que les dotfiles (fichiers cachés) sont inclus dans la copie:

$ cp -Rf foo/. bar
Peut Oakes
la source
1
Est-ce vrai? Ça a l'air inhabituel.
Mateng
2
@Mateng Je viens de le tester - oui c'est vrai.
Matmarbon du
1
.signifie tous les fichiers du répertoire. Donc, naturellement, les fichiers cachés seraient inclus.
Jaspreet Singh
C'est plutôt chouette en fait. Vous pouvez également faire "cp -RTf foo bar" sous Linux.
xpusostomos
5

Très similaire à @Jonathan Wheeler:

Si vous ne voulez pas vous souvenir, mais pas réécrire bar:

rm -r bar/
cp -r foo/ !$

!$ affiche le dernier argument de votre commande précédente.

Michael Dimmitt
la source
4
Le conseil sur le! $ Est génial!
Lucas Morgan
0

Cela devrait résoudre votre problème.

\cp -rf foo/* bar/
Ali
la source
-1

L'opération que vous avez définie est une "fusion" et vous ne pouvez pas le faire avec cp. Cependant, si vous ne cherchez pas à fusionner et que vous êtes d'accord pour perdre le dossier, barvous pouvez simplement rm -rf barsupprimer le dossier, puis mv foo barle renommer. Cela ne prendra pas de temps car les deux opérations sont effectuées par des pointeurs de fichier et non par le contenu du fichier.

Ati
la source
-2

Essayez d'utiliser cette commande composée de deux étapes:

rm -rf bar && cp -r foo bar
Yahor M
la source