web-dev-qa-db-fra.com

Fusionner 2 arbres répertoires à Linux sans copier?

J'ai deux arbres de répertoire 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 voudrais fusionner les arbres de répertoire 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 faire cela à l'aide de la commande "CP", mais je veux déplacer les fichiers au lieu de copier, car les répertoires réels que je veux fusionner sont vraiment volumineux et contiennent de nombreux fichiers (des millions). Si j'utilise "MV", je reçois le "fichier existant" une erreur en raison de noms de répertoires contradictoires.

Mise à jour: Vous pouvez supposer qu'il n'y a aucun fichier en double entre les deux arbres de répertoire.

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

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

28
karmawhore

C'est étrange personne noté que cp a option -l:

 - L, - Link [.____] Les fichiers de liaison durs au lieu de copier 

Vous pouvez faire quelque chose comme

[.____]% Mkdir fusionnez [.____]% cp -rl Dir1/* dir2/* fusionner [.____]% RM -r dir "
 Fusionner [.____] .____.] ├── a 
 │ ├── file1.txt 
 ││── file2.txt [.____] │ ├── file5.txt [.____] └── file6.txt [.____] ├── B [.____] ││── file3.txt [.____] ││── file7.txt8. txt 
 └── c [.____] ├── file10.txt 
 ├── file4.txt 
 └── file9.txt 
 13 répertoires, 0 fichiers [.____]
22
Maximilian

Vous pouvez utiliser Rename (AKA PRENAME, du paquet PERL) pour cela. Méfiez-vous que le nom ne fait pas nécessairement référence à la commande que je décris en dehors de Debian/Ubuntu (bien que ce soit un fichier PERL portable unique 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 plus) et d'éditer les chemins de fichiers à partir de votre éditeur de texte préféré.

5
Tobu

J'aime le rsync et préname Solutions, mais si vous voulez vraiment faire MV faire le travail et

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

ensuite, il est possible de gérer un grand nombre de fichiers pouvant avoir des espaces aléatoires dans leurs noms, tous avec un script shell de style 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
3
Chris Johnsen

J'ai dû faire cela plusieurs fois pour les arbres de code source à différentes étapes de développement. Ma solution était d'utiliser GIT de la manière suivante:

  1. Créez un référentiel git et ajoutez tous les fichiers du dir1.
  2. S'engager
  3. Supprimer tous les fichiers et copier dans les fichiers de dir2
  4. S'engager
  5. Voir les différences entre les deux points de validation et prendre des décisions minutieuses sur la manière dont je souhaite fusionner les résultats.

Vous pouvez la définir avec ramification et ainsi de suite, mais c'est l'idée générale. Et vous avez moins peur de la farcir parce que vous avez un instantané complet de chaque état.

0
user42030