Fusionnez 2 arborescences de directorys sous Linux sans copyr?

J'ai deux arborescences de directorys avec des mises en page similaires, c'est-à-dire

. |-- dir1 | |-- a | | |-- file1.txt | | `-- file2.txt | |-- b | | `-- file3.txt | `-- c | `-- file4.txt `-- dir2 |-- a | |-- file5.txt | `-- file6.txt |-- b | |-- file7.txt | `-- file8.txt `-- c |-- file10.txt `-- file9.txt 

Je souhaite merge les arborescences du directory dir1 et dir2 pour créer:

  merged/ |-- a | |-- file1.txt | |-- file2.txt | |-- file5.txt | `-- file6.txt |-- b | |-- file3.txt | |-- file7.txt | `-- file8.txt `-- c |-- file10.txt |-- file4.txt `-- file9.txt 

Je sais que je peux le faire en utilisant la command "cp", mais je souhaite déplacer les files au lieu de copyr, car les directorys réels que je veux merge sont très volumineux et contiennent beaucoup de files (millions). Si j'utilise "mv", j'obtiens l'erreur "Fichier existe" en raison de noms de directorys contradictoires.

MISE À JOUR: vous pouvez supposer qu'il n'y a pas de files en double entre les deux trees de directorys.

 rsync -ax --link-dest=dir1/ dir1/ merged/ rsync -ax --link-dest=dir2/ dir2/ merged/ 

Cela créerait des liens difficiles plutôt que de les déplacer, vous pouvez vérifier qu'ils ont été déplacés correctement, puis, supprimer dir1/ et dir2/ .

Il est étrange que personne n'ait remarqué que cp ait une option -l :

 -l, --link
        files de liens durs au lieu de copyr

Vous pouvez faire quelque chose comme

 % mkdir fusion
 % cp -rl dir1 / * dir2 / * fusion
 % rm -r dir *
 % fusion d'trees 
 merge
 ├── a a
 │ ├── file1.txt
 │ ├── file2.txt
 │ ├── file5.txt
 │ └── file6.txt
 ├──b
 │ ├── file3.txt
 │ ├── file7.txt
 │ └── file8.txt
 └──c
     ├── file10.txt
     ├── file4.txt
     └── file9.txt

 13 directorys, 0 files

Vous pouvez utiliser le renommer (aka prename, du package perl) pour cela. Attention, le nom ne se réfère pas nécessairement à la command que je décris en dehors de Debian / ubuntu (bien qu'il s'agisse d'un seul file perl portatif si vous en avez besoin).

 mv -T dir1 merged rename 's:^dir2/:merged/:' dir2/* dir2/*/* find dir2 -maxdepth 1 -type d -empty -delete 

Vous avez également la possibilité d'utiliser vidir (à partir de moreutils) et d'éditer les paths de files à partir de votre éditeur de text préféré.

J'aime les solutions rsync et prename , mais si vous voulez vraiment faire mv faire le travail et

  • votre find sait -print0 et -depth ,
  • votre xargs sait -0 ,
  • vous avez printf ,

alors il est possible de gérer un grand nombre de files qui peuvent avoir des espaces randoms dans leurs noms, tous avec un script shell Bourne:

 #!/bin/sh die() { printf '%s: %s\n' "${0##*/}" "$*" exit 127 } maybe='' maybe() { if test -z "$maybe"; then "$@" else printf '%s\n' "$*" fi } case "$1" in -h|--help) printf "usage: %s [-n] merge-dir src-dir [src-dir [...]]\n" "${0##*/}" printf "\n Merge the <src-dir> trees into <merge-dir>.\n" exit 127 ;; -n|--dry-run) maybe=NotRightNow,Thanks.; shift ;; esac test "$#" -lt 2 && die 'not enough arguments' mergeDir="$1"; shift if ! test -e "$mergeDir"; then maybe mv "$1" "$mergeDir" shift else if ! test -d "$mergeDir"; then die "not a directory: $mergeDir" fi fi xtrace='' case "$-" in *x*) xtrace=yes; esac for srcDir; do (cd "$srcDir" && find . -print0) | xargs -0 sh -c ' maybe() { if test -z "$maybe"; then "$@" else printf "%s\n" "$*" fi } xtrace="$1"; shift maybe="$1"; shift mergeDir="$1"; shift srcDir="$1"; shift test -n "$xtrace" && set -x for entry; do if test -d "$srcDir/$entry"; then maybe false >/dev/null && continue test -d "$mergeDir/$entry" || mkdir -p "$mergeDir/$entry" continue else maybe mv "$srcDir/$entry" "$mergeDir/$entry" fi done ' - "$xtrace" "$maybe" "$mergeDir" "$srcDir" maybe false >/dev/null || find "$srcDir" -depth -type d -print0 | xargs -0 rmdir done 

Brute force bash

 #! /bin/bash for f in $(find dir2 -type f) do old=$(dirname $f) new=dir1${old##dir2} [ -e $new ] || mkdir $new mv $f $new done 

le test le fait

 # setup for d in dir1/{a,b,c} dir2/{a,b,c,d} ; do mkdir -p $d ;done touch dir1/a/file{1,2} dir1/b/file{3,4} dir2/a/file{5,6} dir2/b/file{7,8} dir2/c/file{9,10} dir2/d/file11 # do it and look $ find dir{1,2} -type f dir1/a/file1 dir1/a/file2 dir1/a/file5 dir1/a/file6 dir1/b/file3 dir1/b/file7 dir1/b/file8 dir1/c/file4 dir1/c/file9 dir1/c/file10 dir1/d/file11 

J'ai dû faire cela plusieurs fois pour les trees de code source à différents stades de développement. Ma solution était d'utiliser Git de la manière suivante:

  1. Créez un repository git et ajoutez tous les files à partir de dir1.
  2. Commettre
  3. Supprime tous les files et copy dans les files de dir2
  4. Commettre
  5. Regardez les différences entre les deux points de validation et prenez des décisions précises quant à la façon dont je veux merge les résultats.

Vous pouvez le finir avec une ramification et ainsi de suite, mais c'est l'idée générale. Et vous avez less peur de rembourrage car vous avez un aperçu complet de chaque état.