rsync --delete с указанием папки назначения

rsync --delete с указанием папки назначения

У меня есть rsyncпроцесс, который синхронизирует контент из исходного репозитория (который контролируется версиями) в общее монтирование NFS.

Сценарий (каким бы ужасным он ни был) заключается в том, что папка назначения содержит больше контента, чем исходная папка, поскольку другой контент синхронизируется с папкой назначения из разных источников. Так, например, структуры папок могут выглядеть следующим образом:

источник

a/a1.txt
a/a2.txt
b/b1.txt

место назначения

a/a1.txt
a/a2.txt
a/a3.txt
b/b1.txt
c/c1.txt

(в этом примере a/a3.txtи c/c1.txtсинхронизируются с местом назначения из другого места. На практике это включает в себя несколько других источников, и на их содержимое/процессы нельзя повлиять.)

Теперь предположим, что исходная папка удаляет a/a2.txtфайл. Используя существующую настройку, этот файл не будет удален в месте назначения; но использование --deleteприведет к удалению других файлов, и это требование не делать этого.

Как можно --deleteиспользовать этот rsync, но при этом соответствовать требованиям? Поскольку исходный каталог контролируется версиями, достаточно просто получить до и после этого каталога, поэтому дифференциальную резервную копию можно рассчитать, используя исходный исходный каталог в качестве ссылки, но является ли это лучшим способом?

решение1

Вы не можете использовать rsync --deleteего таким образом. Он не сохраняет состояние и не сохраняет записи о том, какие файлы были удалены между запусками. Флаг --deleteпросто указывает rsyncна необходимость удаления каждого файла в месте назначения, которого нет в источнике.

Для реализации этого ограниченного удаления, я думаю, вам нужно поддерживать свое собственное состояние. Ни то, rsyncни другое не unisonможет сделать этого за вас.

Следующий код не является полностью безопасным решением, это лишь отправная точка. (Однако он обрабатывает файлы со странными именами, включая те, которые содержат встроенный символ новой строки.)

Предположим, что есть два каталога srcи dst. (Для целей примера не имеет значения, является ли dstон локальным или удаленным.)

# Find the current list of files (do this just once, to prep the cache)
( cd src && find . -type f -print0 ) | LC_ALL=C sort -z > .state.src

Каждый раз, когда мы выполняем резервное копирование, запускаем следующий код

# Run the rsync to transfer files. "dst/" could be local
rsync -av src/ remote:dst/

# Determine the set of files to delete in "dst/"
( cd src && find . -type f -print0 ) | LC_ALL=C sort -z | tee .state.src.new |
    LC_ALL=C comm -z - -13 .state.src |
    ssh remote 'while IFS= read -d "" -r f; do rm -f "dst/$f"; done'

# That seemed to work, so update the state cache
[[ 0 -eq $? ]] && mv -f .state.src.new .state.src

Если ваша версия comm(как и моя) старше GNU coreutils 8.25 и не имеет этого -zфлага, вы можете использовать этот альтернативный способ:

# Find the current list of files (do this just once, to prep the cache)
( cd src && find . -type f -print0 ) | tr '\0\n' '\n\0' | LC_ALL=C sort > .state.src

Каждый раз, когда мы выполняем резервное копирование, запускаем следующий код

# Run the rsync to transfer files. "dst/" could be local
rsync -av src/ remote:dst/

# Determine the set of files to delete in "dst/"
( cd src && find . -type f  -print0 ) | tr '\0\n' '\n\0' | LC_ALL=C sort | tee .state.src.new |
    LC_ALL=C comm -13 - .state.src |
    tr '\0\n' '\n\0' |
    ssh remote 'while IFS= read -d "" -r f; do rm -f "dst/$f"; done'

# That seemed to work, so update the state cache
[[ 0 -eq $? ]] && mv -f .state.src.new .state.src

Связанный контент