it-swarm.com.de

Ordner mit mv zusammenführen?

Wenn ich mv verwende, um einen Ordner namens "Ordner" in ein Verzeichnis zu verschieben, das bereits "Ordner" enthält, werden sie zusammengeführt oder ersetzt?

157
Dominique

mv kann keine Verzeichnisse zusammenführen oder überschreiben, es schlägt mit der Meldung fehl "mv: 'a' kann nicht nach 'b' verschoben werden: Verzeichnis nicht leer", selbst wenn Sie es sind Verwenden Sie die Option --force.


Sie können dies mit anderen Tools umgehen (z. B. rsync, find oder sogar cp), aber Sie müssen die Auswirkungen sorgfältig abwägen:

  • rsync kann den Inhalt eines Verzeichnisses in ein anderes zusammenführen (idealerweise mit dem --remove-source-files1 Option zum sicheren Löschen nur der Quelldateien, die erfolgreich übertragen wurden, und mit der üblichen Berechtigungs-/Besitz-/Zeiterhaltungsoption -a, wenn Sie dies wünschen)
    aber Dies ist ein vollständiger Kopiervorgang und kann daher sehr festplattenintensiv sein.
  • Derzeit bevorzugte Option: Sie können die Option --link-dest=DIR Von rsync kombinieren (um nach Möglichkeit Hardlinks zu erstellen, anstatt Dateiinhalte zu kopieren ) und --remove-source-files, um eine Semantik zu erhalten, die einer regulären mv sehr ähnlich ist.
    Dazu muss --link-dest Ein absoluter Pfad zum Verzeichnis Quelle angegeben werden (oder ein relativer Pfad vom Ziel = zur Quelle).
    aber dies verwendet --link-dest In unbeabsichtigter Weise (was Komplikationen verursachen kann oder nicht), erfordert Wissen (oder Bestimmen) des absoluten Pfads zur Quelle (als Argument für --link-dest) und hinterlässt erneut eine leere Verzeichnisstruktur, die gemäß bereinigt werden muss 1.
  • Sie können find verwenden, um die Quellverzeichnisstruktur am Ziel nacheinander neu zu erstellen und dann die tatsächlichen Dateien einzeln zu verschieben
    aber dies muss mehrmals durch die Quelle laufen und kann auf Race-Bedingungen stoßen (neue Verzeichnisse werden an der Quelle während des Multi erstellt -Schrittprozess)
  • cp kann feste Links erstellen (einfach ausgedrückt, zusätzliche Zeiger auf dieselbe vorhandene Datei), wodurch ein Ergebnis erstellt wird, das einem Zusammenführen von mv sehr ähnlich ist (und sehr E/A ist -effizient, da nur Zeiger erstellt werden und keine tatsächlichen Daten kopiert werden müssen)
    aber dies leidet wieder unter einer möglichen Racebedingung (neue Dateien an der Quelle werden gelöscht, obwohl sie nicht in die kopiert wurden vorheriger Schritt)

Welche dieser Problemumgehungen (falls vorhanden) angemessen ist, hängt stark von Ihrem spezifischen Anwendungsfall ab.
Denken Sie wie immer nach, bevor Sie einen dieser Befehle ausführen, und erstellen Sie Backups.


1: Beachten Sie, dass rsync --remove-source-files Keine Verzeichnisse löscht, sodass Sie anschließend etwas wie find -depth -type d -empty -delete Ausführen müssen, um den leeren Quellverzeichnisbaum zu entfernen.

127
n.st
rsync -av /source/ /destination/
(after checking)
rm -rf /source/
90
fazie

Du kannst den ... benutzen -l Option des Befehls cp , mit dem Hardlinks von Dateien im selben Dateisystem anstelle von Volldatenkopien erstellt werden. Der folgende Befehl kopiert den Ordner source/folder in einen übergeordneten Ordner (destination), der bereits ein Verzeichnis mit dem Namen folder enthält.

cp -rl source/folder destination
rm -r source/folder

Möglicherweise möchten Sie auch das -P (--no-dereference - Symbolische Links nicht de-referenzieren) oder -a (--archive - alle Metadaten beibehalten, auch -P Option), abhängig von Ihren Bedürfnissen.

68
palswim

Ich würde diese vier Schritte empfehlen:

cd ${SOURCE}; 
find . -type d -exec mkdir -p ${DEST}/\{} \; 
find . -type f -exec mv \{} ${DEST}/\{} \; 
find . -type d -empty -delete

oder noch besser, hier ist ein Skript, das eine ähnliche Semantik wie mv implementiert:

#!/bin/bash

DEST="${@:${#@}}"
ABS_DEST="$(cd "$(dirname "$DEST")"; pwd)/$(basename "$DEST")"

for SRC in ${@:1:$((${#@} -1))}; do   (
    cd "$SRC";
    find . -type d -exec mkdir -p "${ABS_DEST}"/\{} \;
    find . -type f -exec mv \{} "${ABS_DEST}"/\{} \;
    find . -type d -empty -delete
) done
23
Jonathan Mayer

Hier ist eine Möglichkeit, die Verzeichnisse zusammenzuführen. Es ist viel schneller als rsync, da die Dateien nur umbenannt werden, anstatt sie zu kopieren und dann zu löschen.

cd source; find -type f -print0 | xargs -0 -n 1 -I {} mv '{}' 'dest/{}'
16
Jewel

Eine Möglichkeit, dies zu erreichen, wäre:

mv folder/* directory/folder/
rmdir folder

Solange keine zwei Dateien in folder und directory/folder Den gleichen Namen haben, erzielen Sie das gleiche Ergebnis, d. H. Das Zusammenführen.

4
asheeshr

Für die reinsten Kopien verwende ich die Blockread-Kopiermethode tar (-) B.

beispiel aus dem Quellpfad ('cd' dort, falls erforderlich):

tar cBf - <sourcefolder> | (cd /your/target/folder ; tar xBf -)

dadurch wird eine exakte Kopie des Quellbaums erstellt, wobei der Eigentümer und die Berechtigungen intakt sind. Wenn der Zielordner vorhanden ist, werden die Daten zusammengeführt. Nur bereits vorhandene Dateien werden überschrieben.

Beispiel:

 $ cd /data1/home
 $ tar cBf - jdoe | (cd /data2/home ; tar xBf -)

Wenn die Kopieraktion erfolgreich ist, können Sie die Quelle entfernen (rm -rf <source>). Dies ist natürlich kein exakter Schritt: Die Daten werden kopiert, bis Sie die Quelle entfernen.

Als Option können Sie ausführlich sein (die zu kopierende Datei auf dem Bildschirm anzeigen), mit -v: tar cBvf -

  • c: erstellen
  • B: Vollständigen Block lesen (zum Lesen der Pipe)
  • v: ausführlich
  • f: zu schreibende Datei
  • x: extrahieren
  • -: Stdout/stdin

sourcefolder kann auch * sein (für alles im aktuellen Ordner)

4
gjs

Hier ist ein Skript, das für mich funktioniert hat. Ich bevorzuge mv gegenüber rsync, daher verwende ich die Lösungen von Jewel und Jonathan Mayer.

#!/bin/bash

# usage source1 .. sourceN dest

length=$(($#-1))
sources=${@:1:$length}
DEST=$(readlink -f ${!#})
for SRC in "$sources"; do
    pushd "$SRC";
    find . -type d -exec mkdir -p "${DEST}/{}" \;
    find . -type f -exec mv {} "${DEST}/{}" \;
    find . -type d -empty -delete
    popd
done
1
xer0x

Es ist keine gute Idee, Befehle wie cp oder rsync zu verwenden. Bei großen Dateien dauert es lange. mv ist viel schneller, da nur die Inodes aktualisiert werden, ohne die Dateien physisch zu kopieren. Eine bessere Option ist die Verwendung des Dateimanagers Ihres Betriebssystems. Für Opensuse gibt es einen Dateimanager namens Konquerer. Es kann Dateien verschieben, ohne sie tatsächlich zu kopieren. Es hat die Funktion "Ausschneiden und Einfügen" wie in Windows. Wählen Sie einfach alle Unterverzeichnisse in Verzeichnis A aus. Klicken Sie mit der rechten Maustaste und wechseln Sie in Verzeichnis B, das möglicherweise Unterverzeichnisse mit demselben Namen enthält. Es wird sie zusammenführen. Es gibt auch Optionen, ob Sie Dateien mit demselben Namen überschreiben oder umbenennen möchten.

1
simon

Python-Lösung

Da ich keine zufriedenstellende vorbestehende Lösung finden konnte, entschied ich mich, ein schnelles Python Skript) zu erstellen, um dies zu erreichen.

Insbesondere ist diese Methode effizient, da sie den Quelldateibaum nur einmal von unten nach oben durchläuft.

Sie können damit auch Dinge wie das Überschreiben von Dateien schnell nach Ihren Wünschen anpassen.

Verwendungszweck:

move-merge-dirs src/ dest/

verschiebt alle Inhalte von src/* in dest/ und src/ wird verschwinden.

move-merge-dirs

#!/usr/bin/env python3

import argparse
import os

def move_merge_dirs(source_root, dest_root):
    for path, dirs, files in os.walk(source_root, topdown=False):
        dest_dir = os.path.join(
            dest_root,
            os.path.relpath(path, source_root)
        )
        if not os.path.exists(dest_dir):
            os.makedirs(dest_dir)
        for filename in files:
            os.rename(
                os.path.join(path, filename),
                os.path.join(dest_dir, filename)
            )
        for dirname in dirs:
            os.rmdir(os.path.join(path, dirname))
    os.rmdir(source_root)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Move merge src/* into dest. Overwrite existing files.'
    )
    parser.add_argument('src_dir')
    parser.add_argument('dest_dir')
    args = parser.parse_args()
    move_merge_dirs(args.src_dir, args.dest_dir)

GitHub Upstream .

Siehe auch: https://stackoverflow.com/questions/22588225/how-do-you-merge-two-directories-or-move-with-replace-from-the-windows-command

Es macht keinen Sinn, leere Ordner zu kopieren - YMMV

#!/bin/bash

# usage source1 .. sourceN dest

length=$(($#-1))
sources=${@:1:$length}
DEST=$(readlink -f ${!#})
for SRC in $sources; do
    pushd "$SRC";
    # Only one scan - we only need folders with files
    find . -type f | while read FILE ; do
        DIRNAME=`dirname "$FILE"`
        # Create the lowest level directory at once
        if [ ! -d "$DEST/$DIRNAME" ] ; then
            mkdir -v "$DEST/$DIRNAME"
        fi
        mv -v "$FILE" "$DEST/$FILE"
    done
    # Remove the directories no longer needed
    find . -type -d | sort -r | xargs -i rmdir "{}"
    popd
done
  • find nicht mehrfach ausgeführt
  • mkdir -p wird auch ausgeführt, nachdem Verzeichnisse nacheinander gefunden wurden

Dies ist der Befehl zum Verschieben von Dateien und Ordnern an ein anderes Ziel:

$ mv /source/path/folder /target/destination/

Denken Sie daran: mv Befehl funktioniert nicht, wenn der Ordner b̲e̲i̲n̲g m̲e̲r̲ge̲d̲ ist (dh ein anderer Ordner mit demselben Name existiert bereits im Ziel) und das d̲e̲s̲t̲i̲n̲a̲t̲i̲o̲n̲ o̲n̲e̲ i̲s̲ n̲o̲t̲ e̲m̲pt̲y .

mv: '/ source/path/folder' kann nicht nach '/ target/destination/folder' verschoben werden: Verzeichnis nicht leer

Wenn der Zielordner leer ist, funktioniert der obige Befehl einwandfrei.

Um also auf jeden Fall beide Ordner zusammenzuführen,
Entweder in 2 Befehlen:

$ cp -rf /source/path/folder /target/destination/
$ rm -rf /source/path/folder

Oder kombinieren Sie beide als einmaligen Befehl:

$ cp -rf /source/path/folder /target/destination/ && rm -rf /source/path/folder

mv = bewegen
cp = Kopie
rm = entfernen

- r für Verzeichnis (Ordner)
- f Ausführung erzwingen

0
Talha Siddiqi

Sie können a und b zusammenführen mit:

shopt -s dotglob
mv a/* b

Vor mv:

.
├── a
│   ├── c
│   │   └── x
│   └── .x
└── b
    ├── y
    └── .y

Nach mv:

.
├── a
└── b
    ├── c
    │   └── x
    ├── .x
    ├── y
    └── .y

dotglob ermöglicht es Ihnen, versteckte Punktdateien wie . x zu verschieben

Verwenden Sie rmdir, um ein leeres Verzeichnis zu entfernen.

0
sir__finley