web-dev-qa-db-fra.com

Comment copier un dossier de manière récursive de manière idempotente en utilisant cp?

Quand j'utilise

cp -R inputFolder outputFolder

le résultat est dépendant du contexte:

  1. si outputFolder n'existe pas, il sera créé et le chemin du dossier cloné sera outputFolder.
  2. si outputFolder existe, alors le clone créé sera outputFolder/inputFolder

C'est horrible, parce que je veux créer un script d'installation, et si l'utilisateur l'exécute deux fois par erreur, il aura outputFolder créé la première fois, puis lors de la deuxième exécution tout le contenu sera à nouveau créé dans outputFolder/inputFolder.

  • Je veux toujours le premier comportement: créer un clone à côté de l'original (en tant que frère).
  • Je veux utiliser cp pour être portable (par exemple, MINGW n'a pas rsync livré)
  • J'ai vérifié cp -R --parents mais cela recrée le chemin tout au long de l'arborescence des répertoires (donc le clone ne sera pas outputFolder mais some/path/outputFolder)
  • --remove-destination ou --update dans le cas où 2 ne change rien, les choses sont toujours copiées dans outputFolder/inputFolder

Existe-t-il un moyen de le faire sans d'abord vérifier l'existence de outputFolder (si le dossier n'existe pas alors ...) ou en utilisant rm -rf outputFolder?

Quelle est la méthode UNIX portable et convenable de le faire?

46
jakub.g

Utilisez-le à la place:

cp -R inputFolder/. outputFolder

Cela fonctionne exactement de la même manière que, par exemple, cp -R aaa/bbb ccc fonctionne: si ccc n'existe pas, il est créé en tant que copie de bbb et de son contenu; mais si ccc existe déjà alors ccc/bbb est créé en tant que copie de bbb et de son contenu.

Pour presque toutes les instances de bbb, cela donne le comportement indésirable que vous avez noté dans votre question. Cependant, dans cette situation spécifique, le bbb est juste ., donc aaa/bbb est vraiment aaa/., qui à son tour est vraiment juste aaa mais sous un autre nom. Nous avons donc ces deux scénarios:

  1. ccc n'existe pas:

    La commande cp -R aaa/. ccc signifie "créer ccc et copier le contenu de aaa/. en ccc/., c'est-à-dire copier aaa dans ccc.

  2. ccc existe:

    La commande cp -R aaa/. ccc signifie "copier le contenu de aaa/. en ccc/., c'est-à-dire copier aaa dans ccc.

54
roaima

Ne copiez pas le dossier, copiez uniquement le contenu:

## Create the target directory. The -p suppresses error messages
## if the directory already exists
mkdir -p outputFolder

## Copy the contents recursively, this will not recreate the parent
cp -R inputfolder/* outputfolder/

De cette façon, vous vous assurez à la fois que le répertoire cible est créé lors de la première exécution du script et évitez le problème lors de sa deuxième exécution.

Chris Down souligne très correctement qu'en bash, cela sautera les fichiers dont le nom commence par un .. Pour éviter cela, vous pouvez exécuter shopt -s dotglob avant d'exécuter la commande ci-dessus.

Tous les deux -p pour mkdir et -R pour cp sont définis par POSIX donc cela devrait être parfaitement portable.

18
terdon

Essaie le -T option pour cp. Cela existe dans GNU coreutils cp version 8.22; il peut ne pas être portable en dehors de cela.

11
Tom Hunt

Vous pouvez également utiliser rsync:

rsync -uav inputFolder/ outputFolder/

(notez les barres obliques, surtout après la première)

4
Ned64

À mon avis, il n'y a rien de plus simple et de plus portable que rm -rf outputFolder. Donc, je m'en tiens toujours à cela. Je comprends que votre question est différente, mais je pense que c'est la meilleure pratique.

0
dashohoxha

Vous pouvez utiliser l'option -t De la commande cp comme:

cp -R inputFolder -t outputFolder

maintenant, si le dossier cible n'existe pas, il générera une erreur:

cp: failed to access ‘outputFolder’: No such file or directory

la commande ci-dessus copiera inputFolder avec son contenu (et pas seulement son contenu)

si vous voulez copier uniquement le contenu de inputFolder cela devient un peu délicat (car vous devez être prudent lorsque vous utilisez le globbing Shell tout en utilisant un astérisque *)

cp -R  -t outputFolder/ -- inputFolder/*

maintenant, si le dossier cible n'existe pas, il générera une erreur:

cp: failed to access ‘outputFolder’: No such file or directory

fonctionne avec cp (GNU coreutils) 8.23

0
Edward Torvalds