rsync: синхронизирует папки, но сохраняет дополнительные файлы в целевой папке

10

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

rsync -a --delete "${source_dir}" "${target_dir}";

Это делает содержание цели точно таким же, как содержимое источника. Однако я хотел бы иметь возможность добавлять некоторые файлы к цели, а не к источнику, но я не хочу, чтобы они удалялись каждый раз, когда я выполняю rsync. С другой стороны, файлы, которые были синхронизированы, а затем удалены в источнике, должны быть удалены.

Есть ли способ сделать это без необходимости изменять команду для каждого файла, который я хочу исключить?

Обновление : я должен упомянуть, что я не ограничен rsync. Если другая программа выполняет свою работу, это тоже хорошо. Я просто попытался решить эту проблему с помощью rsync.

jkrzefski
источник
Привет @AszunesHeart, просто любопытно, но вы проверили ответ (ы)?
Джейкоб Влейм
Вы пытались убрать опцию --delete? Это похоже на параметр / MIR в Robocopy.
SDsolar

Ответы:

9

rsyncесть опция с именем --exclude-fromoption, которая позволяет вам создать файл, содержащий список любых файлов, которые вы хотели бы исключить. Вы можете обновить этот файл всякий раз, когда хотите добавить новое исключение или удалить старое.

Если вы создадите исключающий файл в /home/user/rsync_excludeновой команде будет:

rsync -a --delete --exclude-from="/home/user/rsync_exclude" "${source_dir}" "${target_dir}"

При создании файла списка исключений вы должны поместить каждое правило исключения в отдельную строку. Исключения относятся к вашему исходному каталогу. Если ваш /home/user/rsync_excludeфайл содержал следующие параметры:

secret_file
first_dir/subdir/*
second_dir/common_name.*
  • Любой файл или каталог, вызванный secret_fileв вашем исходном каталоге, будет исключен.
  • Любые файлы в ${source_dir}/first_dir/subdirбудут исключены, но пустая версия subdirбудет синхронизирована.
  • Любые файлы ${source_dir}/second_dirс префиксом common_name.будут игнорироваться. Так common_name.txt, и common_name.jpgт.д.
Arronical
источник
Я не уверен, что это делает то, что я хотел. Также я считаю нецелесообразным перечислять все файлы или папки, которые добавляются к цели. Я бы предпочел иметь автоматический способ сделать это. Допустим, у меня есть разные сценарии в target, которые генерируют несколько лог-файлов (также в target), и я не хочу перечислять каждое местоположение этих файлов в rsync_exclude-file. Есть ли способ заставить rsync «запомнить», какие файлы были синхронизированы, и позволить --delete затрагивать только эти файлы?
Jkrzefski
Извините, я неправильно прочитал ваш вопрос, хотя я хотел, чтобы вы добавили его в исходный код, а те не обновились до целевого. Я думаю, что есть способ сделать то, что вы хотите, но мне придется немного подумать. Я прокомментирую, как только у меня будет время для редактирования.
Arronical
@jkrzefski Если вы создаете файлы из другого сценария в target и хотите исключить их из источника, то почему бы не изменить место назначения этих файлов журнала в другую папку? Предположительно, если вы не синхронизируете их, это потому, что они менее важны.
6

Поскольку вы упомянули: я не ограничен rsync:

Скрипт для поддержки зеркала, позволяющий добавлять дополнительные файлы к цели

Ниже сценарий, который делает именно то, что вы описываете.

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

Подробный вариант

введите описание изображения здесь


Концепт

1. При первом резервном копировании скрипт:

  • создает файл (в целевом каталоге), в котором перечислены все файлы и каталоги; .recentfiles
  • создает точную копию (зеркало) всех файлов и каталогов в целевом каталоге

2. На следующем и т. Д. Бэкапе

  • Скрипт сравнивает структуру каталогов и дату (ы) изменения файлов. Новые файлы и каталоги в источнике копируются в зеркало. В то же время создается второй (временный) файл, в котором перечислены текущие файлы и каталоги в исходном каталоге; .currentfiles,
  • Впоследствии .recentfiles(перечисление ситуации в предыдущей резервной копии) сравнивается с .currentfiles. Только файлы, из .recentfilesкоторых нет .currentfiles, очевидно удаляются из источника и будут удалены из цели.
  • Файлы, которые вы вручную добавили в целевую папку, в любом случае не «видны» сценарием и остаются одни.
  • Наконец, временное имя .currentfilesпереименовывается для .recentfilesобслуживания следующего цикла резервного копирования и так далее.

Сценарий

#!/usr/bin/env python3
import os
import sys
import shutil

dr1 = sys.argv[1]; dr2 = sys.argv[2]

# --- choose verbose (or not)
verbose = True
# ---

recentfiles = os.path.join(dr2, ".recentfiles")
currentfiles = os.path.join(dr2, ".currentfiles")

if verbose:
    print("Counting items in source...")
    file_count = sum([len(files)+len(d) for r, d, files in os.walk(dr1)])
    print(file_count, "items in source")
    print("Reading directory & file structure...")
    done = 0; chunk = int(file_count/5); full = chunk*5

def show_percentage(done):
    if done % chunk == 0:
        print(str(int(done/full*100))+"%...", end = " ")

for root, dirs, files in os.walk(dr1):
    for dr in dirs:
        if verbose:
            if done == 0:
                print("Updating mirror...")
            done = done + 1
            show_percentage(done) 
        target = os.path.join(root, dr).replace(dr1, dr2)
        source = os.path.join(root, dr)
        open(currentfiles, "a+").write(target+"\n")
        if not os.path.exists(target):
            shutil.copytree(source, target)
    for f in files:
        if verbose:
            done = done + 1
            show_percentage(done)
        target = os.path.join(root, f).replace(dr1, dr2)
        source = os.path.join(root, f)
        open(currentfiles, "a+").write(target+"\n") 
        sourcedit = os.path.getmtime(source)
        try:
            if os.path.getmtime(source) > os.path.getmtime(target):
                shutil.copy(source, target)   
        except FileNotFoundError:
            shutil.copy(source, target)

if verbose:
    print("\nChecking for deleted files in source...")

if os.path.exists(recentfiles):
    recent = [f.strip() for f in open(recentfiles).readlines()]
    current = [f.strip() for f in open(currentfiles).readlines()]
    remove = set([f for f in recent if not f in current])
    for f in remove:
        try:
            os.remove(f)
        except IsADirectoryError:
            shutil.rmtree(f)
        except FileNotFoundError:     
            pass
        if verbose:
            print("Removed:", f.split("/")[-1])

if verbose:
    print("Done.")

shutil.move(currentfiles, recentfiles)

Как пользоваться

  1. Скопируйте скрипт в пустой файл, сохраните его как backup_special.py
  2. Измените, если хотите, подробный параметр в заголовке скрипта:

    # --- choose verbose (or not)
    verbose = True
    # ---
    
  3. Запустите его с источником и целью в качестве аргументов:

     python3 /path/to/backup_special.py <source_directory> <target_directory>
    

скорость

Я протестировал сценарий в каталоге на 10 ГБ с примерно 40 000 файлов и каталогов на моем сетевом диске (NAS), он сделал резервное копирование в то же время, что и rsync.

Обновление всего каталога заняло всего несколько секунд больше, чем rsync, для 40 000 файлов, что неприемлемо, и это неудивительно, поскольку сценарию необходимо сравнить содержимое с последней сделанной резервной копией.

Якоб Влейм
источник
Привет @ Aszune'sHeart добавил сценарий вариант. Пожалуйста, укажите, если все ясно.
Яков Влийм