dossiers de fusion linux: rsync?

13

J'ai deux copies d'un dossier

src/
dest/

Je veux les fusionner en procédant comme suit:

Si un fichier se trouve uniquement dans src, je souhaite qu'il soit déplacé versdest

Si un fichier est uniquement dans dest, je veux qu'il soit ignoré IE laissé seul.

Si un fichier est dans les deux et a un contenu identique (IE même taille et date), supprimez desrc

Si un fichier est dans les deux et n'a pas un contenu identique, laissez-le srcdedans pour que je puisse les fusionner manuellement.

Seul un très petit nombre de fichiers (entre 0% et 5% du nombre total de fichiers) devrait appartenir à cette dernière catégorie, mais je ne sais pas comment séparer le dans les deux et le même dans les deux, mais différent.

J'ai essayé de comprendre comment le faire, rsyncmais en vain jusqu'à présent.

David Oneill
la source

Réponses:

17

Je n'ai effectué que des tests de fonctionnalité limités, alors soyez prudent avec cette commande (--dry-run):

rsync -avPr --ignore-existing --remove-source-files src/ dest

Veuillez noter la fin / car cela va récursivement dans src au lieu de copier src lui-même, cela devrait conserver vos chemins existants.

En utilisant l'indicateur --ignore-existing en combinaison avec l'indicateur --remove-source-files, vous supprimerez uniquement les fichiers de src qui sont synchronisés de src vers dest, c'est-à-dire des fichiers qui n'existaient pas auparavant dans dest uniquement.

Pour supprimer les fichiers non synchronisés, c'est-à-dire ceux qui existaient déjà dans dest / comme dans src /, vous pouvez utiliser:

for file in `find src/ -type f`; do diff $file `echo $file | sed 's/src/dest/'` && rm $file || echo $file; done

ou

find src -type f -exec bash -c 'cmp -s "$0" "${0/#src/dest}" && rm "$0"' {} \;

si les noms de fichiers peuvent contenir des espaces / de nouvelles lignes /… En ce qui concerne le commentaire de Gilles concernant les caractères spéciaux, c'est certainement quelque chose à prendre en compte et il existe de nombreuses solutions, la plus simple serait de passer un -i à rm qui demandera avant toute suppression. À condition que src /, ou son chemin parent, soit fourni pour trouver, cependant, le chemin d'accès complet doit permettre à tous les noms de fichiers d'être traités correctement par les commandes diff et rm sans guillemets.

Tok
la source
correction: cette commande ne supprimera pas les fichiers de src si une copie identique existe déjà dans dest
Tok
Ouais :(. C'est la partie que j'ai du mal à comprendre.
David Oneill
2
Eh bien, la bonne nouvelle est que vous pouvez le résoudre indépendamment sans trop de tracas: for file in `find src/ -type f`; do diff $file `echo $file | sed 's/src/dest/'` && rm $file || echo $file; done(vous pouvez ignorer le || echo $filesi vous le souhaitez, il est inclus pour être complet)
Tok
Nifty: c'est ce dont j'avais besoin. Modifiez cela dans votre réponse, et je l'accepterai!
David Oneill
@Tok: Votre commande s'étouffera avec les noms de fichiers qui contiennent des caractères spéciaux (espace,, \?*[initial -). Vous devez utiliser des guillemets doubles autour des substitutions de variables , passer --aux utilitaires avant les noms de fichiers, utiliser find … -exec …au lieu d'analyser la sortie de find. Avec une rmcommande dans le mélange, c'est une recette pour un désastre.
Gilles 'SO- arrête d'être méchant'
6

l'unisson est l'outil que vous recherchez. Essayez unison-gtk si vous préférez un gui. Mais je ne pense pas que cela supprimera des fichiers similaires: essayez à l'unisson d'avoir les deux répertoires identiques. Néanmoins, il 1) identifiera facilement les fichiers à copier; 2) lesquels nécessitent une fusion manuelle.

simonp
la source
Il ne fait pas exactement ce que le PO demande, mais il semble qu'il atteigne le but ultime du PO. +1
Ryan C. Thompson
+1 Malheureusement, le serveur sur lequel j'exécute ce système n'a pas installé à l'unisson et je n'ai pas les autorisations pour l'installer. Mais cela pourrait être une bonne réponse à quelqu'un d'autre.
David Oneill
1
Vous pouvez télécharger l'exécutable unison depuis seas.upenn.edu/~bcpierce/unison//download/… . Installez-le quelque part dans votre répertoire personnel, c'est juste un fichier.
JooMing
2

Le script suivant devrait faire les choses raisonnablement. Il déplace les fichiers de la source vers la destination, sans jamais écraser un fichier et créer des répertoires si nécessaire. Les fichiers source qui ont un fichier différent correspondant dans la destination sont laissés seuls, tout comme les fichiers qui ne sont pas des fichiers ou des répertoires normaux (par exemple des liens symboliques). Les fichiers restants dans la source sont ceux pour lesquels il existe un conflit. Attention, je ne l'ai pas testé du tout.

cd src
find . -exec sh -c '
    set -- "/path/to/dest/$0"
    if [ -d "$0" ]; then #  the source is a directory 
      if ! [ -e "$1" ]; then
        mv -- "$0" "$1"  # move whole directory in one go
      fi
    elif ! [ -e "$0" ]; then  # the source doesn't exist after all
      :  # might happen if a whole directory was moved
    elif ! [ -e "$1" ]; then  # the destination doesn't exist
      mv -- "$0" "$1"
    elif [ -f "$1" ] && cmp -s -- "$0" "$1"; then  # identical files
      rm -- "$0"
    fi
  ' {} \;

Une autre approche serait de faire un montage d'union un répertoire au-dessus de l'autre, par exemple avec funionfs ou unionfs-fuse .

Gilles 'SO- arrête d'être méchant'
la source