Wie überschreibe ich Zieldateien mit mv?

155

Ich habe eine Menge Dateien und Verzeichnisse in einem Unterverzeichnis, das ich in das übergeordnete Verzeichnis verschieben möchte. Es gibt bereits einige Dateien und Verzeichnisse im Zielverzeichnis, die überschrieben werden müssen. Dateien, die nur im Ziel vorhanden sind, sollten nicht verändert werden. Kann ich das erzwingen mv? Es ( mv * ..) beschwert sich

mv: cannot move `xyz' to `../xyz': Directory not empty

Was vermisse ich?

EricSchaefer
quelle
3
Hast du es versucht mv -f?
Sakisk
Ich frage mich, warum mv -fnicht die richtige Antwort ist.
Pedro Lobito
@PedroLobito: Weil es nicht funktioniert? -f überschreibt nur Dateien, funktioniert jedoch nicht, wenn Sie Unterverzeichnisse verschieben, die im Ziel vorhanden und nicht leer sind.
EricSchaefer

Antworten:

118

Sie haben sie zum Ziel kopieren und dann die Quelle löschen, mit den Befehlen cp -r * ..gefolgt von rm -rf *.

Ich glaube nicht, dass Sie Verzeichnisse mit "zusammenführen" können mv.

Dogbane
quelle
4
Nun, das wollte ich nicht, weil es lange dauern wird ... Trotzdem danke.
EricSchaefer
12
Vermutlich mvist es schneller, weil Sie sich im selben Dateisystem befinden? Was ist, wenn Sie cp -lHardlinks erstellen, anstatt die Dateien tatsächlich zu verschieben?
Mattdm
6
Sie sollten cp -astatt cp -rverwenden, um die Dateiattribute (Zeitstempel, Berechtigungen usw.) beizubehalten.
Dotancohen
2
Für Leute, die spät über Google anreisen, ahmt die Antwort unten von @palswim das Verhalten von mv nach, indem sie neue feste Links zu den Daten erstellt und dann die alten Links löscht. Kurze Antwort cp -rl source destination && rm -r source.
William Everett
72

rsyncwäre wahrscheinlich eine bessere Option hier. Es ist so einfach wie rsync -a subdir/ ./.

Mein Testbaum in filename: contentsFormat:

./file1:root
./file2:root
./dir/file3:dir
./dir/file4:dir
./subdir/dir/file3:subdir
./subdir/file1:subdir

Laufen rsync:

$ rsync -a -v subdir/ ./
sending incremental file list
./
file1
dir/
dir/file3

Gibt:

./file1:subdir
./file2:root
./dir/file3:subdir
./dir/file4:dir
./subdir/dir/file3:subdir
./subdir/file1:subdir

Und dann, um zu emulieren mv, möchten Sie wahrscheinlich das Quellverzeichnis entfernen:

$ rm -r subdir/

Geben:

./file1:subdir
./file2:parent
./dir/file3:subdir
./dir/file4:dir

Wenn dies falsch ist, können Sie bitte ein ähnliches Beispiel (z. B. mithilfe meines Testbaums oben in dieser Antwort) mit dem gewünschten Ergebnis angeben?

Mikel
quelle
1
Rsync-Kopien. Diese Frage handelt vom Umzug.
Gilles
1
@ Gilles: Danke. Ich habe rm -ram Ende hinzugefügt , um es im Grunde das gleiche wie zu machen mv.
Mikel
3
copy-then-delete entspricht nicht mv, wenn sich Quelle und Ziel im selben Dateisystem befinden. mvIst atomar, behält Inode-Nummern bei (sodass die Datei geöffnet bleiben kann) und benötigt keine Zeit und keinen Platz, um eine Kopie zu erstellen.
Gilles
5
@ Gilles: Mir ist klar, aber derzeit ist die führende Antwort cp -r; rm -r. Ich denke in diesem Sinne rsyncist es auch erwähnenswert.
Mikel
Ich habe es schon mit cp / rm gemacht (es war dringend). Es hat wirklich lange gedauert. Gilles Drehbuch wäre wahrscheinlich viel schneller gewesen, aber er war zu spät.
EricSchaefer
47

rsynckann die Quelle nach dem Kopieren mit dem --remove-source-filesParameter löschen . Dies sollte eine bequeme Möglichkeit sein, das zu tun, was Sie möchten.

Aus dem rsync man page:

        --remove-source-files   sender removes synchronized files (non-dir)
Mike Chen
quelle
Das ist wirklich die beste Antwort. Ich konnte cp -r; rmwegen des Mangels an freiem Speicherplatz nicht verwenden . Stattdessen haben rsync --remove-source-filesbeide das Kopieren über exakt die gleichen Dateien vermieden und den belegten Speicherplatz minimiert.
Guaka
20

Sie können dies mit cpund tun rm, aber ohne die enorme Datenmenge zu kopieren, die Sie (vermutlich) vermeiden möchten, zu übertragen. @mattdm spielte in seinem Kommentar darauf an , und eine Antwort auf eine andere Frage enthält eine ausführlichere Diskussion über verschiedene Optionen.

cp -rlf source destination
rm -r source

Im Wesentlichen erstellt die -lOption für den cpBefehl feste Verknüpfungen zu Dateien, anstatt deren Daten in neue Dateien zu kopieren.

Kumpelschwimmen
quelle
1
Ich weiß, dass das OP seine Aufgabe , die zur Beantwortung der Frage geführt hat, bereits erledigt hat , aber hoffentlich kann diese Antwort in Zukunft jedem helfen, der mit diesem Problem zu tun hat.
Palswim
Das war wirklich die Antwort, die sie brauchten. Ich habe das gerade mit 60 GB von Tausenden kleiner Cyrus-E-Mail-Dateien gemacht und es dauerte nur 21 Sekunden.
Labradort
Dies ist auch der Weg, den ich eingeschlagen habe - ich habe ihn nur geändert, cp -al source destinationum die Informationen und Berechtigungen des Besitzers zu erhalten.
piit79
Ich denke, um tatsächlich das zu tun, wonach OP gefragt hat, muss die Option -f hinzugefügt werden, sonst wird die Datei nicht überschrieben, wenn sie existiert. Kannst du das bestätigen oder mache ich etwas falsch?
das Keks
@dasKeks: Eigentlich cpwird standardmäßig überschrieben. Die -fOption versucht, die Datei (wie in rm) vor dem erneuten Kopieren zu entfernen. Dies kann hilfreich sein, wenn der Prozess die Datei nicht zum Schreiben öffnen kann. Einige Benutzer möchten jedoch lieber feststellen, dass stattdessen ein Fehler aufgetreten ist. Ich weiß nicht, ob es für das OP von Bedeutung ist, aber ich -fhabe meiner Antwort trotzdem die Flagge hinzugefügt .
Palswim
8

Hier ist ein Skript, das Dateien von unten /path/to/source/rootin den entsprechenden Pfad unter verschiebt /path/to/destination/root.

  • Wenn sowohl in der Quelle als auch im Ziel ein Verzeichnis vorhanden ist, wird der Inhalt rekursiv verschoben und zusammengeführt.
  • Wenn eine Datei oder ein Verzeichnis in der Quelle, aber nicht im Ziel vorhanden ist, wird es verschoben.
  • Alle Dateien oder Verzeichnisse, die bereits im Ziel vorhanden sind, bleiben zurück. (Insbesondere zusammengeführte Verzeichnisse bleiben in der Quelle zurück. Dies ist nicht einfach zu beheben.)

Vorsicht, ungetesteter Code.

export dest='/path/to/destination/root'
cd /path/to/source/root
find . -type d \( -exec sh -c '[ -d "$dest/$0" ]' \; -o \
                  -exec sh -c 'mv "$0" "$dest/$0"' {} \; -prune \) \
    -o -exec sh -c '
        if ! [ -e "$dest/$0" ]; then
          mv -f "$0" "$dest/$0"
        fi
' {} \;
Gilles
quelle
Zwei Korrekturen: Sie brauchen ein \;vor dem -oin der ersten Zeile des findBefehls, und Sie sollten nicht !in der if- es ist nur !nicht\!
llhuii
2

Dieser Thread ist seit Jahren da draußen und rangiert immer noch auf Platz 1 bei Google, also wollte ich eine andere Methode hinzufügen. So mache ich das normalerweise: Packe den Inhalt des Unterverzeichnisses in ein Tarball, verschiebe das Tarball in das übergeordnete Verzeichnis und extrahiere es dann mit dem Standardverhalten --overwrite. Das macht genau das, wonach Sie suchen. Anschließend können Sie Ihr Unterverzeichnis entfernen.

cd xyz
tar -cvzpf tmp.tar.gz *
mv tmp.tar.gz ../tmp.tar.gz
cd ..
tar -xvzpf tmp.tar.gz
rm -rf xyz
rm -f tmp.tar.gz
Simon Kraus
quelle
Dies funktioniert nur, wenn Sie über zusätzlichen Speicherplatz für die komprimierte TAR-Datei verfügen. Und möchten Sie die temporäre TAR-Datei nach dem Extrahieren wirklich behalten?
Anthon
Bearbeiteter Code zum Entfernen der tmp-Datei. Heutzutage ist der Platz in den meisten Fällen nicht das Problem. #terabyteages
Simon Kraus
0

Wenn Sie über genügend Speicherplatz verfügen, können Sie dies folgendermaßen tun:

mv -bfv directory_1/* directory_2/ # all duplicate source files/directories 
                                   # will have ~ appended to them
find -name "*~" -delete            # will recursively find and delete all files 
                                   # with ~ on the end

Vergewissern Sie sich, dass keine wichtigen Dateien mit einem ~ am Ende versehen sind, aber falls vorhanden, können Sie --suffix=whateveryouwantanstelle der Standarddatei hinzufügen .

4D3H2
quelle