
У меня есть 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