Jak nadpisać pliki docelowe mv?

154

Mam mnóstwo plików i katalogów w podkatalogu Chcę przenieść do katalogu nadrzędnego. W katalogu docelowym jest już kilka plików i katalogów, które należy zastąpić. Pliki obecne tylko w celu powinny pozostać nietknięte. Czy mogę to zmusić mv? To ( mv * ..) narzeka

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

czego mi brakuje?

EricSchaefer
źródło
3
Próbowałeś mv -f?
sakisk
Zastanawiam się, dlaczego mv -fnie jest poprawna odpowiedź.
Pedro Lobito
@PedroLobito: Ponieważ to nie działa? -f tylko nadpisuje pliki, ale nie działa, jeśli przenosisz podkatalogi, które istnieją w miejscu docelowym i nie są puste.
EricSchaefer

Odpowiedzi:

117

Będziesz musiał skopiować je do miejsca docelowego, a następnie usunąć źródło, używając poleceń cp -r * ..poprzedzonych rm -rf *.

Nie sądzę, że można „scalić” katalogi za pomocą mv.

dogbane
źródło
4
Cóż, tego nie chciałem robić, ponieważ zajmie to dużo czasu ... W każdym razie dzięki.
EricSchaefer
12
Przypuszczalnie mvjest szybszy, ponieważ korzystasz z tego samego systemu plików? Co jeśli użyjesz cp -ldo tworzenia twardych dowiązań, a nie przenoszenia plików?
mattdm
6
Należy użyć cp -azamiast cp -r, aby zachować atrybuty pliku (znacznik czasu, uprawnienia itp.).
dotancohen
2
Dla osób przybywających tutaj późno przez Google odpowiedź poniżej @palswim emuluje zachowanie mv, tworząc nowe twarde linki do danych, a następnie usuwając stare linki. Krótka odpowiedź cp -rl source destination && rm -r source.
William Everett
72

rsyncprawdopodobnie byłaby lepszym rozwiązaniem tutaj. To takie proste jak rsync -a subdir/ ./.

Moje drzewo test filename: contentsformat:

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

Bieganie rsync:

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

Daje:

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

A następnie, aby emulować mv, prawdopodobnie chcesz usunąć katalog źródłowy:

$ rm -r subdir/

Dający:

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

Jeśli to źle, czy możesz podać podobny przykład (np. Używając mojego drzewa testowego z góry tej odpowiedzi) z pożądanym rezultatem?

Mikel
źródło
1
kopie rsync. To pytanie dotyczy przeprowadzki.
Gilles
1
@Gilles: Dzięki. Dodałem rm -rna końcu, aby zasadniczo był taki sam jak mv.
Mikel
3
Kopiuj, a następnie usuń nie jest równoważne mv, gdy źródło i miejsce docelowe znajdują się w tym samym systemie plików. mvjest atomowy, zachowuje numery i-węzłów (aby plik mógł pozostać otwarty) i nie zajmuje czasu ani miejsca na wykonanie kopii.
Gilles
5
@Gilles: Zdaję sobie z tego sprawę, ale obecnie wiodącą odpowiedzią jest cp -r; rm -r. Myślę, że w tym sensie też rsyncwarto wspomnieć.
Mikel
Zrobiłem to już z cp / rm (to było pilne). To naprawdę zajęło dużo czasu. Skrypt Gillesa prawdopodobnie byłby o wiele szybszy, ale też się spóźnił.
EricSchaefer
47

rsyncmożna usunąć źródło po kopiach z --remove-source-filesparametrem. To powinien być wygodny sposób na robienie tego, co chcesz.

Z rsync man page:

        --remove-source-files   sender removes synchronized files (non-dir)
Mike Chen
źródło
To naprawdę najlepsza odpowiedź. Nie mogłem korzystać z cp -r; rmpowodu braku wolnego miejsca. Zamiast tego rsync --remove-source-filesoba zminimalizowane zużyte miejsce na dysku pozwoliło uniknąć kopiowania dokładnie tych samych plików.
guaka
20

Możesz to zrobić za pomocą cpi rm, ale bez kopiowania ogromnej ilości danych (prawdopodobnie) próbujesz uniknąć przeniesienia. @mattdm wspomniał o tym w swoim komentarzu , a odpowiedź na inne pytanie zawiera pełniejszą dyskusję na temat różnych opcji.

cp -rlf source destination
rm -r source

Zasadniczo -lopcja cppolecenia tworzy twarde łącza do plików zamiast kopiowania ich danych do nowych plików.

palswim
źródło
1
Wiem, że OP już wykonał swoje zadanie, które skłoniło do pytania, ale mam nadzieję, że ta odpowiedź może pomóc każdemu z tym problemem w przyszłości.
palswim
To była naprawdę odpowiedź, której potrzebowali. Właśnie to zrobiłem z 60 GB tysięcy małych plików e-mail cyrus i zajęło to tylko 21 sekund.
labradort
To także trasa, którą wybrałem - właśnie zmieniłem ją, cp -al source destinationaby zachować informacje o właścicielu i uprawnienia.
piit79,
Myślę, że aby właściwie zrobić to, o co poprosił OP, musisz dodać opcję -f, w przeciwnym razie nie nadpisze, jeśli plik istnieje. Czy możesz to potwierdzić, czy robię coś złego?
das Keks
@dasKeks: W rzeczywistości cpdomyślnie zastępuje. Ta -fopcja próbuje usunąć plik (jak w rm) przed próbą ponownego skopiowania, co może pomóc, jeśli proces nie może otworzyć pliku do zapisu, chociaż niektórzy woleliby, aby zamiast tego wystąpił błąd. Nie wiem, czy to miało znaczenie dla PO, ale i tak dodałem -fflagę do mojej odpowiedzi.
palswim
8

Oto skrypt, który przenosi pliki z dołu /path/to/source/rootdo odpowiedniej ścieżki pod /path/to/destination/root.

  • Jeśli katalog istnieje zarówno w źródłowym, jak i docelowym, zawartość jest rekurencyjnie przenoszona i scalana.
  • Jeśli plik lub katalog istnieje w źródle, ale nie w miejscu docelowym, zostanie on przeniesiony.
  • Każdy plik lub katalog, który już istnieje w miejscu docelowym, zostaje pozostawiony. (W szczególności połączone katalogi pozostają w źródle. Nie jest to łatwe do naprawienia).

Uwaga, nie przetestowany kod.

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
źródło
Dwie poprawki: potrzebujesz \;przed -opierwszym wierszem findpolecenia i nie powinieneś uciekać !w if- to po prostu !, nie\!
llhuii 20'11
2

Wątek ten istnieje od lat i wciąż zajmuje pierwsze miejsce w Google, więc chciałem dodać inną metodę. Jak zwykle to robię: pakowanie zawartości podkatalogu do tarballa, przenoszenie tarballa do katalogu nadrzędnego, a następnie wypakowanie go z domyślnym zachowaniem - overwrite. To robi dokładnie to, czego szukasz. Następnie możesz usunąć swój podkatalog.

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
źródło
Działa to tylko wtedy, gdy masz dodatkowe miejsce na skompresowany plik tar. I czy naprawdę chcesz zachować tymczasowy plik tar po rozpakowaniu?
Anthon
Edytowano kod, aby usunąć plik tmp. W dzisiejszych czasach przestrzeń nie jest problemem w większości przypadków. #terabyteages
Simon Kraus,
0

Jeśli masz wystarczającą ilość miejsca, możesz to zrobić w następujący sposób:

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

Upewnij się, że nie ma żadnych ważnych plików z ~ na ich końcu, ale jeśli są, możesz dodać --suffix=whateveryouwantzamiast domyślnego.

4D3H2
źródło