Fusionner 2 arborescences sous Linux sans les copier?


35

J'ai deux arbres de répertoire avec des dispositions similaires, à savoir

.
 |-- 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 fusionner les arborescences de répertoires 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 commande "cp", mais je souhaite déplacer les fichiers au lieu de les copier, car les répertoires que je souhaite fusionner sont vraiment volumineux et contiennent beaucoup de fichiers (des millions). Si j'utilise "mv", l'erreur "Fichier existe" est générée en raison de noms de répertoire en conflit.

UPDATE: Vous pouvez supposer qu'il n'y a pas de fichiers en double entre les deux arborescences.


Etes-vous sûr qu'il n'y a pas de duplication de noms de fichiers entre les deux dossiers? que voulez-vous qu'il se passe s'il y a des doublons?
Zoredache

Si vous avez littéralement des millions de fichiers dans un seul répertoire, vous devez envisager de les scinder en plusieurs sous-répertoires pour des raisons de performances - bien que cela ne soit pas pertinent pour la question posée.
DrStalker

Réponses:


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

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


9
Genre de. En réalité, il ne duplique aucune utilisation du disque, il crée simplement un autre pointeur sur le même fragment de disque et ne "copie" en fait aucune donnée. (Voir en.wikipedia.org/wiki/Hard_links ) Cependant, il doit effectuer cette opération une fois par fichier. Mais c’est essentiellement ce que toutes ces réponses finissent de faire, car vous ne pouvez pas déplacer un seul répertoire.
Christopher Karel

1
Comme il ne nécessite pas la surcharge de la copie de fichiers, c'est une solution parfaitement acceptable.
Tobu le

2
Cela ne fonctionne que s’ils se trouvent sur le même système de fichiers. Est-ce que rsync avec l'option de suppression effectuerait un déplacement s'ils se trouvaient sur le même système de fichiers? (c’est-à-dire qu’il suffit de changer les informations du répertoire, mais pas de déplacer le fichier).
Ronald Pottol

1
rsync va copier, puis supprimer s'il traverse des systèmes de fichiers.
Karmawhore

5
Une mise en garde: rendre le --link-destchemin absolu, ou relatif à merged/; ou il va copier.
Tobu

21

C'est étrange personne n'a noté qui cpa l'option -l:

-l, --link
       fichiers de liens durs au lieu de copier

Vous pouvez faire quelque chose comme

% mkdir fusion
% cp -rl dir1 / * dir2 / * fusion
% rm -r dir *
% de fusion d'arbres 
fusionner
├── un
├── fichier1.txt
├── fichier2.txt
├── ├── fichier5.txt
6. └── fichier6.txt
├── b
├── fichier3.txt
├── fichier7.txt
8. └── fichier8.txt
└── c
    ├── fichier10.txt
    ├── fichier4.txt
    └── fichier9.txt

13 répertoires, 0 fichiers

Cela ne fonctionne pas sur différents disques durs ...
Alex Leach

4
Il est plus correct de dire que cela ne fonctionne pas sur plusieurs systèmes de fichiers, car ceux-ci peuvent s'étendre sur plusieurs disques durs. De plus, si op veut éviter de copier les fichiers, c’est une bonne chose qui cp -lne fonctionne pas entre systèmes de fichiers.
lvella

2
Vous voudrez peut-être utiliser cp -a(synonyme de cp -RPp) pour conserver tous les attributs des fichiers et éviter de suivre les liens symboliques: la commande devient ici cp -al dir1/* dir2/* merge.
Tricasse

5

Vous pouvez utiliser renommer (ou prename, du paquet perl) pour cela. Attention, 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 seul fichier perl portable 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 de modifier les chemins de fichiers à partir de votre éditeur de texte préféré.


3

Je aime les rsync et PRFNOM solutions, mais si vous voulez vraiment faire mv faire le travail et

  • votre trouvaille sait -print0et -depth,
  • votre xargs sait -0,
  • vous avez printf ,

il est alors possible de gérer un grand nombre de fichiers dont les noms peuvent contenir des espaces aléatoires, le tout avec un script shell de type 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

Vous pouvez dire à xargs de limiter ses entrées à la nouvelle ligne et d'ignorer la traduction. Par exemple, ce qui suit trouve et supprime tous vos fichiers torrent dans le répertoire actuel, même ceux avec des caractères Unicode ou un autre type de tromperie. find . -name '*.torrent' | xargs -d '\n' rm
PRS

2

Force brute 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

test fait cela

# 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

2
L'OP a spécifié des millions de fichiers, ce qui risque de briser cette construction. En outre, il ne gérera pas correctement les noms de fichiers avec des espaces, des nouvelles lignes, etc.
Chris Johnsen

0

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

  1. Créez un référentiel git et ajoutez tous les fichiers de dir1.
  2. Commettre
  3. Supprimer tous les fichiers et copier dans des fichiers de dir2
  4. Commettre
  5. Affichez les différences entre les deux points de validation et prenez des décisions judicieuses concernant la manière dont je souhaite fusionner les résultats.

Vous pouvez le raffiner avec des branches, etc., mais c’est l’idée générale. Et vous avez moins peur de tout gâcher parce que vous avez un instantané complet de chaque état.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.