2 Verzeichnisbäume in Linux zusammenführen ohne zu kopieren?

35

Ich habe zwei Verzeichnisbäume mit ähnlichen Layouts, dh

.
 |-- 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

Ich möchte die Verzeichnisbäume dir1 und dir2 zusammenführen, um Folgendes zu erstellen:

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

Ich weiß, dass ich dies mit dem Befehl "cp" tun kann, aber ich möchte die Dateien verschieben, anstatt sie zu kopieren, da die tatsächlichen Verzeichnisse, die ich zusammenführen möchte, sehr groß sind und viele Dateien (Millionen) enthalten. Wenn ich "mv" verwende, erhalte ich den Fehler "Datei existiert", weil in Konflikt stehende Verzeichnisnamen vorliegen.

UPDATE: Sie können davon ausgehen, dass sich zwischen den beiden Verzeichnisbäumen keine doppelten Dateien befinden.

bajafresh4life
quelle
Sind Sie sicher, dass zwischen den beiden Ordnern keine doppelten Dateinamen vorhanden sind? Was soll geschehen, wenn Duplikate vorhanden sind?
Zoredache
Wenn Sie buchstäblich Millionen von Dateien in einem einzigen Verzeichnis haben, sollten Sie aus Leistungsgründen die Aufteilung der Dateien in separate Unterverzeichnisse in Betracht ziehen - obwohl dies für die tatsächlich gestellte Frage irrelevant ist.
DrStalker

Antworten:

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

Dadurch würden Hardlinks erstellt, anstatt sie zu verschieben. Sie können überprüfen, ob sie ordnungsgemäß verschoben wurden. Entfernen Sie dann dir1/und dir2/.

karmawhore
quelle
9
So'ne Art. Es wird keine Festplattennutzung dupliziert, es wird lediglich ein weiterer Zeiger auf dieselbe Festplatte erstellt und es werden keine Daten "kopiert". (Siehe en.wikipedia.org/wiki/Hard_links ) Diese Operation muss jedoch einmal pro Datei ausgeführt werden. Aber genau das ist es, was all diese Antworten bewirken, da Sie nicht nur ein einziges Verzeichnis verschieben können.
Christopher Karel
1
Da es nicht den io-Overhead für das Kopieren von Dateien gibt, ist dies eine absolut akzeptable Lösung.
Tobu
2
Dies funktioniert jedoch nur, wenn sie sich im selben Dateisystem befinden. Würde rsync mit der Option delete eine Verschiebung durchführen, wenn sie sich auf demselben Dateisystem befinden? (Das heißt, ändern Sie einfach die Verzeichnisinformationen, aber verschieben Sie die Datei nicht).
Ronald Pottol
1
rsync kopiert und löscht dann, wenn es Dateisysteme durchquert.
Karmawhore
5
Eine Einschränkung: Machen Sie den --link-destPfad absolut oder relativ zu merged/; oder es wird kopiert.
Tobu
21

Es ist seltsam, dass niemand bemerkt cphat , dass es eine Option gibt -l:

-l, --link
       Hardlink-Dateien anstatt zu kopieren

Sie können so etwas tun

% mkdir merge
% cp -rl Verzeichnis1 / * Verzeichnis2 / * zusammenführen
% rm -r dir *
% tree merge 
verschmelzen
├── a
│ ├── Datei1.txt
│ ├── file2.txt
│ ├── file5.txt
│ └── file6.txt
├── b
│ ├── file3.txt
│ ├── file7.txt
│ └── file8.txt
└── c
    ├── file10.txt
    ├── file4.txt
    └── file9.txt

13 Verzeichnisse, 0 Dateien
Maximilian
quelle
Dies funktioniert nicht auf verschiedenen Festplatten ...
Alex Leach
4
Es ist richtiger zu sagen, dass es nicht zwischen Dateisystemen funktioniert, da sich Dateisysteme über mehrere Festplatten erstrecken können. Wenn Sie vermeiden möchten, dass Dateien kopiert werden, cp -lfunktioniert dies nicht systemübergreifend.
lvella
2
Möglicherweise möchten Sie cp -a(synonym zu cp -RPp) verwenden, um alle Attribute der Dateien beizubehalten und folgende Symlinks zu vermeiden: Hier wird der Befehl cp -al dir1/* dir2/* merge.
Tricasse
5

Sie können dafür Rename (aka prename, aus dem Perl-Paket) verwenden. Beachten Sie, dass der Name nicht unbedingt auf den Befehl verweist, den ich außerhalb von debian / ubuntu beschreibe (obwohl es sich um eine einzelne portable Perl-Datei handelt, wenn Sie sie benötigen).

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

Sie haben auch die Möglichkeit, vidir (aus moreutils) zu verwenden und die Dateipfade in Ihrem bevorzugten Texteditor zu bearbeiten.

Tobu
quelle
3

Ich mag die Lösungen rsync und prename , aber wenn Sie wirklich wollen, dass mv die Arbeit macht und

  • Ihr Fund weiß -print0und -depth,
  • Ihre Xargs wissen -0,
  • du hast printf ,

Dann ist es möglich, eine große Anzahl von Dateien mit zufälligen Leerzeichen im Namen zu verarbeiten, und zwar alle mit einem Shell-Skript im Bourne-Stil:

#!/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
Chris Johnsen
quelle
Sie können xargs anweisen, die Eingabe auf newline zu beschränken und die Übersetzung zu überspringen. Im Folgenden werden beispielsweise alle Ihre Torrent-Dateien im aktuellen Verzeichnis gesucht und gelöscht, auch solche mit Unicode-Zeichen oder einer anderen Tomfoolery. find . -name '*.torrent' | xargs -d '\n' rm
PRS
2

Rohe Gewalt 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 macht das

# 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
David J. Liszewski
quelle
2
Das OP spezifizierte Millionen von Dateien, was diese Konstruktion wahrscheinlich zerstören wird. Außerdem werden Dateinamen mit Leerzeichen, Zeilenumbrüchen usw. nicht richtig behandelt.
Chris Johnsen
0

Ich musste dies mehrmals für Quellcodebäume in verschiedenen Entwicklungsstadien tun. Meine Lösung bestand darin, Git folgendermaßen zu verwenden:

  1. Erstellen Sie ein Git-Repository und fügen Sie alle Dateien aus dir1 hinzu.
  2. Verpflichten
  3. Entfernen Sie alle Dateien und kopieren Sie die Dateien aus dir2
  4. Verpflichten
  5. Zeigen Sie die Unterschiede zwischen den beiden Festschreibungspunkten an und treffen Sie sorgfältige Entscheidungen darüber, wie ich die Ergebnisse zusammenführen möchte.

Sie können es mit Verzweigungen und so weiter verfeinern, aber dies ist die allgemeine Idee. Und Sie haben weniger Angst davor, es zu stopfen, weil Sie eine vollständige Momentaufnahme jedes Zustands haben.


quelle