linuksowe foldery scalające: rsync?

13

Mam dwie kopie folderu

src/
dest/

Chcę je scalić, wykonując następujące czynności:

Jeśli plik jest tylko w srcśrodku, chcę go przenieśćdest

Jeśli plik jest tylko w destśrodku, chcę, aby IE go zignorował.

Jeśli plik jest w obu i ma identyczną zawartość (IE tego samego rozmiaru i daty), usuń zsrc

Jeśli plik znajduje się w obu i nie ma identycznej zawartości, pozostaw je, srcaby móc je ręcznie scalić.

Tylko bardzo mała liczba plików (od 0% do 5% wszystkich plików) powinna należeć do tej ostatniej kategorii, ale nie wiem, jak oddzielić oba w jednym i tym samym od obu, ale różne.

Próbowałem dowiedzieć się, jak to zrobić, rsyncale jak dotąd bezskutecznie.

David Oneill
źródło

Odpowiedzi:

17

Przeprowadziłem tylko ograniczone testy funkcjonalności, więc proszę uważaj na to polecenie (--dry-run):

rsync -avPr --ignore-existing --remove-source-files src/ dest

Proszę zwrócić uwagę na trailing /, ponieważ to ponownie pojawi się w src zamiast kopiowania samego src, to powinno zachować twoje istniejące ścieżki.

Używając flagi --ignore-istniejącej w połączeniu z flagą --remove-source-files, usuniesz tylko pliki z src, które są zsynchronizowane z src na dest, czyli pliki, które wcześniej nie istniały tylko w dest.

Aby usunąć pliki niezsynchronizowane, czyli te, które już istniały w dest / as w src /, możesz użyć:

for file in `find src/ -type f`; do diff $file `echo $file | sed 's/src/dest/'` && rm $file || echo $file; done

lub

find src -type f -exec bash -c 'cmp -s "$0" "${0/#src/dest}" && rm "$0"' {} \;

jeśli nazwy plików mogą zawierać białe znaki / nowe wiersze /… Jeśli chodzi o komentarz Gillesa dotyczący znaków specjalnych, to z pewnością należy o tym pamiętać i istnieje wiele rozwiązań, najprostszym byłoby przekazanie -i do rm, które wyświetli monit przed usunięciem. Jednak pod warunkiem, że src / lub jego ścieżka nadrzędna znajduje się w celu znalezienia, w pełni kwalifikowana ścieżka powinna spowodować, że wszystkie nazwy plików będą poprawnie obsługiwane zarówno przez polecenia diff, jak i rm bez cudzysłowu.

Tok
źródło
korekta: to polecenie nie usunie plików z src, jeśli identyczna kopia już istnieje w dest
Tok
Tak :(. Trudno mi to zrozumieć.
David Oneill,
2
Dobra wiadomość jest taka, że ​​możesz rozwiązać go samodzielnie bez większych problemów: for file in `find src/ -type f`; do diff $file `echo $file | sed 's/src/dest/'` && rm $file || echo $file; done(możesz pominąć, || echo $filejeśli chcesz, jest to uwzględnione dla kompletności)
Tok
Zręczne: tego potrzebowałem. Edytuj to w swojej odpowiedzi, a ja to zaakceptuję!
David Oneill
@Tok: Twoje polecenie dusi nazwy plików zawierające znaki specjalne (białe znaki \?*[, inicjał -). Musisz używać podwójnych cudzysłowów wokół podstawień zmiennych , przechodzić --do narzędzi przed nazwami plików, find … -exec …zamiast analizować wynik find. Z rmpoleceniem w miksie jest to przepis na katastrofę.
Gilles „SO- przestań być zły”
6

unison to narzędzie, którego szukasz. Spróbuj unison-gtk, jeśli wolisz GUI. Ale nie sądzę, że usunie podobne pliki: zgodnie staraj się, aby oba katalogi były identyczne. Niemniej jednak łatwo 1) zidentyfikuje pliki do skopiowania; 2) które wymagają ręcznego scalenia.

simonp
źródło
Nie robi dokładnie tego, o co prosi OP, ale wydaje się, że osiąga ostateczny cel PO. +1
Ryan C. Thompson
+1 Niestety, serwer, na którym działam, nie ma zainstalowanego unison, ani nie mam uprawnień do jego instalacji. Ale może to być dobra odpowiedź dla kogoś innego.
David Oneill,
1
Możesz pobrać plik wykonywalny unison z seas.upenn.edu/~bcpierce/unison//download/… . Zainstaluj go gdzieś w swoim katalogu domowym, to tylko jeden plik.
JooMing
2

Poniższy skrypt powinien działać rozsądnie. Przenosi pliki ze źródła do miejsca docelowego, nigdy nie nadpisując pliku i tworząc katalogi w razie potrzeby. Pliki źródłowe, które mają odpowiadający inny plik w miejscu docelowym, zostają pozostawione same, podobnie jak pliki, które nie są zwykłymi plikami lub katalogami (np. Dowiązania symboliczne). Pliki pozostałe w źródle to te, w przypadku których występuje konflikt. Uwaga, wcale tego nie testowałem.

cd src
find . -exec sh -c '
    set -- "/path/to/dest/$0"
    if [ -d "$0" ]; then #  the source is a directory 
      if ! [ -e "$1" ]; then
        mv -- "$0" "$1"  # move whole directory in one go
      fi
    elif ! [ -e "$0" ]; then  # the source doesn't exist after all
      :  # might happen if a whole directory was moved
    elif ! [ -e "$1" ]; then  # the destination doesn't exist
      mv -- "$0" "$1"
    elif [ -f "$1" ] && cmp -s -- "$0" "$1"; then  # identical files
      rm -- "$0"
    fi
  ' {} \;

Innym podejściem byłoby zrobienie montowania unii jeden katalog nad drugim, na przykład z funionfs lub unionfs -fuse .

Gilles „SO- przestań być zły”
źródło