web-dev-qa-db-fra.com

Comment puis-je effectuer une opération de «copie en cas de modification»?

Je voudrais copier un ensemble de fichiers du répertoire A vers le répertoire B, avec la mise en garde que si un fichier dans le répertoire A est identique à un fichier dans le répertoire B, ce fichier ne doit pas être copié (et donc son heure de modification ne doit pas être mise à jour). Existe-t-il un moyen de le faire avec les outils existants, sans écrire mon propre script pour le faire?

Pour développer un peu mon cas d'utilisation: je suis en train de générer automatiquement un tas de .c fichiers dans un répertoire temporaire (par une méthode qui doit tous les générer inconditionnellement), et quand je les recréerai, je voudrais copier uniquement ceux qui ont changé dans le répertoire source réel, en laissant inchangé ceux intacts (avec leurs anciens temps de création) pour que make sache qu'il n'a pas besoin de les recompiler. (Tous les fichiers générés ne sont pas .c fichiers, cependant, j'ai donc besoin de faire des comparaisons binaires plutôt que des comparaisons de texte.)

(Remarque: cela découle de la question que j'ai posée sur https://stackoverflow.com/questions/8981552/speeding-up-file-comparions-with-cmp-on-cygwin/8981762#8981762 , où j'essayais d'accélérer le fichier de script que j'utilisais pour faire cette opération, mais il me vient à l'esprit que je devrais vraiment demander s'il y a une meilleure façon de le faire que d'écrire mon propre script - d'autant plus que tout simple manière de faire cela dans un script Shell invoquera quelque chose comme cmp sur chaque paire de fichiers, et démarrer tous ces processus prend trop de temps.)

37
Brooks Moses

rsync est probablement le meilleur outil pour cela. Il y a beaucoup d'options sur cette commande, alors lisez page de manuel . Je pense que vous voulez l'option --checksum ou --ignore-times

31
Adam Terrey

Vous pouvez utiliser le -u passez à cp comme ceci:

$ cp -u [source] [destination]

Depuis la page de manuel:

   -u, --update
       copy only when the SOURCE file is newer than the destination file or 
       when the destination file is missing
19
gu1

En utilisant rsync --checksum est un bon moyen général de "copier si modifié", dans votre cas particulier, il existe une solution encore meilleure!

Si vous voulez éviter de recompiler inutilement des fichiers, vous devez utiliser ccache qui a été construit à cet effet! En fait, non seulement cela évitera les recompilations inutiles de vos fichiers générés automatiquement, mais cela accélérera également les choses à chaque fois que vous make clean et recompiler à partir de zéro.

Ensuite, je suis sûr que vous demanderez: "Est-ce sûr?" Eh bien, oui, comme le souligne le site Web:

Est-ce sûr?

Oui. L'aspect le plus important d'un cache de compilateur est de toujours produire exactement la même sortie que le vrai compilateur produirait. Cela inclut de fournir exactement les mêmes fichiers objets et exactement les mêmes avertissements du compilateur qui seraient produits si vous utilisez le vrai compilateur. La seule façon de savoir que vous utilisez ccache est la vitesse.

Et c'est facile à utiliser en l'ajoutant simplement comme préfixe dans le CC= ligne de votre makefile (ou vous pouvez utiliser des liens symboliques, mais la façon de makefile est probablement meilleure).

7
aculich

Cela devrait faire ce dont vous avez besoin

diff -qr ./x ./y | awk '{print $2}' | xargs -n1 -J% cp % ./y/

Où:

  • x est votre dossier mis à jour/nouveau
  • y est la destination vers laquelle vous souhaitez copier
  • awk prendra le deuxième argument de chaque ligne de la commande diff (peut-être aurez-vous besoin de trucs supplémentaires pour les noms de fichiers avec de l'espace - ne pouvez pas l'essayer maintenant)
  • xargs -J% insérera le nom du fichier dans cp au bon endroit
4
Patkos Csaba

J'aime utiliser nisson en faveur de rsync car il prend en charge plusieurs maîtres, ayant déjà configuré séparément mes clés ssh et vpn.

Donc, dans ma crontab d'un seul hôte, je les laisse se synchroniser toutes les 15 minutes:

*/15 * * * * [-z "$ (pidof unison)"] && (timeout 25m unison -sortbysize -ui text -batch -times/home/master ssh: //192.168.1.12//home/master -path dev -logfile /tmp/sync.master.dev.log) &> /tmp/sync.master.dev.log

Ensuite, je peux développer de chaque côté et les changements se propageront. En fait, pour les projets importants, j'ai jusqu'à 4 serveurs reflétant la même arborescence (3 exécutent à l'unisson de cron, pointant vers celui qui ne fonctionne pas). En fait, les hôtes Linux et Cygwin sont mixtes - sauf ne vous attendez pas à ce que les liens logiciels dans Win32 soient hors de l'environnement cygwin.

Si vous suivez cette voie, faites le miroir initial sur le côté vide sans le -batch, c'est à dire.

unison -ui text  -times /home/master ssh://192.168.1.12//home/master -path dev

Bien sûr, il existe une configuration pour ignorer les fichiers de sauvegarde, les archives, etc.:

 ~/.unison/default.prf :
# Unison preferences file
ignore = Name {,.}*{.sh~}
ignore = Name {,.}*{.rb~}
ignore = Name {,.}*{.bak}
ignore = Name {,.}*{.tmp}
ignore = Name {,.}*{.txt~}
ignore = Name {,.}*{.pl~}
ignore = Name {.unison.}*
ignore = Name {,.}*{.Zip}

    # Use this command for displaying diffs
    diff = diff -y -W 79 --suppress-common-lines

    ignore = Name *~
    ignore = Name .*~
    ignore = Path */pilot/backup/Archive_*
    ignore = Name *.o
3
Marcos

Tandis que rsync --checksum est la bonne réponse, notez que cette option est incompatible avec --times, et cela --archive comprend --times, donc si vous voulez rsync -a --checksum, vous devez vraiment rsync -a --no-times --checksum.

1
Vladimir Kornea