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?

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.


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/

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.


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

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/{}'

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.


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)


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

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.


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


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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.