Scalanie folderów z mv?

149

Jeśli użyję mvdo przeniesienia folderu o nazwie „folder” do katalogu, który już zawiera „folder”, czy zostaną scalone, czy zastąpione?

Dominique
źródło

Odpowiedzi:

120

mvnie można scalić ani zastąpić katalogów, zakończy się niepowodzeniem z komunikatem „mv: nie można przenieść„ a ”do„ b ”: katalog nie jest pusty” , nawet jeśli korzystasz z tej --forceopcji.


Możesz obejść ten problem za pomocą innych narzędzi (takich jak rsync, finda nawet cp), ale musisz dokładnie rozważyć konsekwencje:

  • rsyncmoże łączyć zawartość jednego katalogu w inny (najlepiej z opcją --remove-source-files1 , aby bezpiecznie usunąć tylko te pliki źródłowe, które zostały pomyślnie przesłane, oraz ze zwykłą opcją zezwolenia / posiadania / zachowania czasu, -ajeśli chcesz)
    ale jest to operacja pełnego kopiowania , i dlatego może być bardzo wymagający na dysku.
  • Obecnie preferowane opcja: Można łączyć rsync„s --link-dest=DIRopcję (aby utworzyć hardlinki zamiast kopiowania zawartości plików, o ile to możliwe) i --remove-source-filesdostać semantyczny bardzo podobny do regularnych mv.
    W tym --link-destcelu należy podać bezwzględną ścieżkę do katalogu źródłowego (lub ścieżkę względną od miejsca docelowego do źródła ).
    Ale wykorzystuje to --link-destw niezamierzony sposób (co może, ale nie musi powodować powikłań), wymaga znajomości (lub ustalenia) bezwzględnej ścieżki do źródła (jako argumentu --link-dest) i ponownie pozostawia pustą strukturę katalogów do wyczyszczenia jako na 1 .
  • Możesz użyćfind do sekwencyjnego odtworzenia struktury katalogu źródłowego w miejscu docelowym, a następnie pojedynczego przeniesienia rzeczywistych plików
    ale to musi wielokrotnie powtarzać się w źródle i może napotkać warunki wyścigu (nowe katalogi są tworzone u źródła podczas procesu wieloetapowego )
  • cpmoże tworzyć twarde linki (po prostu dodatkowe wskaźniki do tego samego istniejącego pliku), co daje wynik bardzo podobny do scalania mv(i jest bardzo wydajny we / wy, ponieważ tworzone są tylko wskaźniki i nie trzeba kopiować żadnych danych)
    ... ale to ponownie cierpi z powodu możliwego wyścigu (nowe pliki u źródła są usuwane, mimo że nie zostały skopiowane w poprzednim kroku)

To, które z tych obejść (jeśli w ogóle) jest odpowiednie, będzie bardzo zależeć od konkretnego przypadku użycia.
Jak zawsze, zastanów się, zanim wykonasz dowolne z tych poleceń, i wykonaj kopie zapasowe.


1: Pamiętaj, że rsync --remove-source-filesnie usunie żadnych katalogów, więc będziesz musiał zrobić coś takiego, find -depth -type d -empty -deleteaby pozbyć się pustego drzewa katalogów źródłowych.

n.st
źródło
Wygląda na to, że wypróbowałeś tylko jedną implementację mv. Ta odpowiedź byłaby lepsza z szerszą prawdą. Linux, BSD i „prawdziwy” Unix lub referencje z POSIX lub SUS.
Warren Young
@WarrenYoung Masz rację, wypróbowałem tylko mvimplementację używaną przez Debiana - nacisk został położony na wypróbowanie , ponieważ strona nie wspomina o tym zachowaniu ...
n
38
Wadą rsync jest to, że faktycznie kopiuje dane, a nie tylko zmienia twardy link, co potencjalnie wymaga dużych zasobów, jeśli masz do czynienia z dużą ilością danych.
Jonathan Mayer
7
@ Keith Uwaga: --deleteusuwa tylko pliki z katalogu docelowego , które nie istnieją w katalogu źródłowym.
n.
1
@JonathanMayer rsync jako kilka funkcji związanych z linkami twardymi. Na przykład możesz po prostu zachować twarde dowiązania za pomocą -Hfunkcji lub możesz dowiązać pliki w miejscu docelowym za pomocą --link-dest. Jednak przed skorzystaniem z nich zajrzyj na stronę manuala.
allo
87
rsync -av /source/ /destination/
(after checking)
rm -rf /source/
etap
źródło
Czy to usunie pliki źródłowe jak w komentarzu n.st?
Dominique
3
Nie, ze względów bezpieczeństwa wolałbym zrobić to w dwóch etapach. Połączone i usunięte źródło jest nieodwracalne. Potrzebny jest również krok dodawania w n. Pierwszej odpowiedzi (aby usunąć katalogi).
przebieg
3
--remove-source-filesma tę zaletę, że usuwa tylko pliki, które zostały pomyślnie przesłane, dzięki czemu można użyć finddo usunięcia pustych katalogów i pozostanie ze wszystkim, co nie zostało przesłane, bez konieczności sprawdzania rsyncdanych wyjściowych.
n
4
Ale tak naprawdę się nie porusza - wpływ na szybkość jest ogromny, jeśli w grę wchodzą duże pliki.
Alex
Ale tak naprawdę nie można wykonać czystego ruchu i scalania.
przebieg
64

Możesz użyć -lopcji polecenia cp , która tworzy twarde łącza plików w tym samym systemie plików zamiast kopii pełnych danych. Następujące polecenie kopiuje folder source/folderdo folderu nadrzędnego ( destination), który już zawiera katalog o nazwie folder.

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

Możesz także użyć -P( --no-dereference- nie usuwaj odsyłaczy symbolicznych) lub -a( --archive- zachowaj wszystkie metadane, w tym także -Popcję), w zależności od potrzeb.

palswim
źródło
7
@rautamiekka: Zakładam, że pytasz o powód korzystania z twardych linków. Jeśli nie wiesz, jakie są twarde linki i dlaczego powinieneś ich używać, prawdopodobnie nie powinieneś wybierać tej trasy. Jednak tworzenie twardych dowiązań nie robi pełnej kopii, więc ta operacja zajęłaby rząd wielkości mniej czasu niż pełna kopia. I wolisz używać twardych linków niż miękkich linków, abyś mógł usunąć pliki źródłowe i nadal mieć poprawne dane zamiast wskaźników do nieprawidłowych ścieżek. I cpraczej niż rsyncodkąd każdy system ma cpi każdy zna go.
palswim
7
Błyskotliwość tego rozwiązania może być uważana za nieakceptowaną odpowiedź. To eleganckie rozwiązanie. Otrzymasz zdolność scalania cpz czasem operacji mv.
TheHerk,
2
jeśli wiesz, że nie musisz przenosić plików, które już istnieją w miejscu docelowym, również chcesz dodać-n
ndemou
1
@ Ruslan: To prawda, ale nie można poruszać się bez kopii między systemami plików przy użyciu dowolnej metody. Nawet mv /fs1/file /fs2/(w różnych systemach plików) wykona kopię, a następnie usunie.
palswim
2
Zgadza się, ale dopóki mvbędzie działać (pod warunkiem, że docelowy katalog jeszcze nie istnieje), nawet jeśli nie będzie „wydajnie” lub jakkolwiek go nazwiesz, cp -rlzawiedzie.
Ruslan
23

Poleciłbym te cztery kroki:

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

lub jeszcze lepiej, oto skrypt, który implementuje semantykę podobną do mv:

#!/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
Jonathan Mayer
źródło
Args to ŹRÓDŁO, DEST
schuess
To wygląda całkiem przydatne. Kusi mnie, aby użyć go do czyszczenia dysku twardego. Czy inni eksperci mogą wypowiedzieć się na ten temat, zanim powierzę skryptowi kilka kopii zapasowych? :-)
LarsH,
BTW, jeśli chcesz zrobić ekwiwalent rsync -u(aktualizuj tylko, jeśli nowszy), mv(przynajmniej w niektórych wersjach), możesz również skorzystać z tej -uopcji. Jednak w takim przypadku możesz usunąć niepuste katalogi źródłowe, a także puste, aby uwzględnić przypadki, w których pliki w drzewie źródłowym nie są nowsze. @schuess: Wygląda na to, że może być wiele argumentów SOURCE, jeśli jest to potrzebne.
LarsH
1
To nie obsługuje dobrze białych znaków. Próbowałem z kilkoma katalogami ze spacjami i skończyłem z nieskończoną serią zagnieżdżonych katalogów.
rofer
16

Oto sposób, który połączy katalogi. Jest znacznie szybszy niż rsync, ponieważ po prostu zmienia nazwy plików zamiast je kopiować, a następnie usuwać.

cd source; find -type f -print0 | xargs -0 -n 1 -I {} mv '{}' 'dest/{}'
Klejnot
źródło
To interesujące, ale tylko nieznacznie związane z tematem, a nawet zdalnie o to, o co pytał użytkownik.
Shadur
17
W rzeczywistości kod Jewel robi dokładnie to, o co prosił użytkownik, z wyjątkiem tworzenia brakujących katalogów. Może powinieneś spojrzeć jeszcze raz?
Jonathan Mayer
3
Dodałbym, żeby użyć „-print0” w find i „-0” w xargs, ponieważ istnieją pliki ze spacjami w nazwach. Istnieje również mały problem, jeśli nazwa zawiera nawias, nie zostanie przeniesiona.
markuz,
2
Jest to o wiele szybsze niż rsync dla niewielkiej liczby plików, ale forsuje nowy proces dla każdego pliku, więc wydajność jest ogromna przy dużej liczbie małych plików. Odpowiedź @ palswim nie dotyczy tego problemu.
b0fh
2
Polecenie zakończy się niepowodzeniem, jeśli in destjest już katalogiem o takiej samej nazwie jak in source. I pliki zostaną przeniesione do dest, który jest w source. Komenda nie robi nic więcejmv source/* source/dest/.
ceving
3

Jednym ze sposobów osiągnięcia tego jest użycie:

mv folder/* directory/folder/
rmdir folder

Dopóki nie ma dwóch plików o tej samej nazwie folderi directory/folder, osiągniesz ten sam wynik, tj. Scalenie.

asheeshr
źródło
3
Jak dokładnie rm folderdziała?
JakeGould
5
@JakeGould W ogóle nie. :)
n
rm folder -fRzawsze działa dla mnie
Octopus,
2
Pamiętaj, że to nie zadziała w przypadku ukrytych plików
b0fh
2

Do najczystszych kopii używam metody kopiowania bloków tar (-) B.

przykład z wewnątrz ścieżki źródłowej (w razie potrzeby „cd”):

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

tworzy to dokładną kopię drzewa źródłowego, Z nienaruszonym właścicielem i uprawnieniami. A jeśli folder docelowy istnieje, dane zostaną scalone. Tylko pliki, które już istnieją, zostaną zastąpione.

Przykład:

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

Gdy operacja kopiowania zakończy się powodzeniem, możesz usunąć source ( rm -rf <source>). Oczywiście nie jest to dokładny ruch: dane będą kopiowane, dopóki nie usuniesz źródła.

Opcjonalnie możesz być gadatliwy (wyświetl na ekranie kopiowany plik), przy pomocy -v: tar cBvf -

  • c: Stwórz
  • B: czytaj pełny blok (do odczytu potoku)
  • v: pełny
  • f: plik do zapisu
  • x: wyciąg
  • -: stdout / stdin

sourcefoldermoże być również *(dla czegokolwiek w bieżącym folderze)

gjs
źródło
Określenie f -tar jest zwykle niepotrzebne - domyślnie jest to odczyt ze stdin / write na stdout.
muru
1

Oto skrypt, który zadziałał dla mnie. Wolę mv niż rsync, więc używam rozwiązań Jewel i 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
xer0x
źródło
To rozwiązanie nie usuwa właściwie nazw ścieżek, bądź ostrożny.
user12439,
@ user12439, zaktualizuję rozwiązanie, jeśli pokażesz mi, którą część mam naprawić.
xer0x
1

Używanie poleceń takich jak cp lub rsync nie jest dobrym pomysłem. W przypadku dużych plików zajmie to dużo czasu. mv jest znacznie szybszy, ponieważ aktualizuje tylko i-węzły bez fizycznego kopiowania plików. Lepszą opcją jest użycie menedżera plików w systemie operacyjnym. W opensuse istnieje menedżer plików o nazwie Konquerer. Może przenosić pliki bez ich kopiowania. Ma funkcję „wycinaj i wklej” jak w systemie Windows. Wystarczy wybrać wszystkie podkatalogi w katalogu A. Kliknij prawym przyciskiem myszy i przejdź do katalogu B, który może zawierać podkatalogi o tych samych nazwach. Połączy je. Istnieją również opcje, czy chcesz zastąpić lub zmienić nazwę plików o tej samej nazwie.

Szymon
źródło
1
OP pyta, co się stanie, gdy mvzostanie użyty.
don_crissti
0

Rozwiązanie Python

Ponieważ nie mogłem znaleźć zadowalającego wcześniej istniejącego rozwiązania, postanowiłem zrobić szybki skrypt Pythona, aby to osiągnąć.

W szczególności ta metoda jest wydajna, ponieważ podchodzi do drzewa plików źródłowych tylko raz oddolnie.

Umożliwi to także szybkie dostosowanie ustawień, takich jak obsługa nadpisywania plików do własnych upodobań.

Stosowanie:

move-merge-dirs src/ dest/

przeniesie całą zawartość src/*do dest/i src/zniknie.

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 w górę .

Zobacz także: https://stackoverflow.com/questions/22588225/how-do-you-merge-two-directories-or-move-with-replace-from-the-windows-command

Ciro Santilli
źródło
0

To polecenie służy do przenoszenia plików i folderów do innego miejsca docelowego:

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

Pamiętaj : mvpolecenie nie będzie działać, jeśli folder jest b̲e̲i̲n̲g m̲e̲r̲ge̲d̲ (tzn. Inny folder o tej samej nazwie już istnieje w miejscu docelowym), a d̲e̲s̲t̲i̲n̲a̲t̲i̲o̲n̲ o̲n̲e̲ i̲s̲ n̲o̲t̲ e̲m̲pt̲y .

mv: nie można przenieść „/ source / path / folder” do „/ target / destination / folder”: katalog nie jest pusty

Jeśli folder docelowy jest pusty, powyższe polecenie będzie działać poprawnie.

Tak więc, aby scalić oba foldery w każdym przypadku,
Zrób to za pomocą 2 poleceń:

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

Lub połącz oba jako jednorazowe polecenie:

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

mv = przenieś
cp = skopiuj
rm = usuń

-r dla katalogu (folderu)
-f wymusza wykonanie

Talha Siddiqi
źródło