rsync: sincroniza pastas, mas mantém arquivos extras no destino

rsync: sincroniza pastas, mas mantém arquivos extras no destino

Estou começando rsynce tentei usá-lo para manter duas pastas no sistema local sincronizadas. Eu tenho uma pasta de origem, cujo conteúdo muda com o tempo (alguns arquivos são adicionados, alguns alterados e outros excluídos) e uma pasta de destino que quero que seja quase um espelho da fonte. Então o que tentei foi usar o rsync assim:

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

Isso mantém o conteúdo do destino exatamente igual ao conteúdo da origem. No entanto, gostaria de poder adicionar alguns arquivos ao destino e não à origem, mas não quero que eles sejam excluídos sempre que fizer o rsync. Por outro lado, os arquivos que costumavam ser sincronizados e depois excluídos na fonte ainda devem ser excluídos.

Existe uma maneira de fazer isso sem precisar alterar o comando para cada arquivo que desejo excluir?

Atualizar: Devo mencionar que não estou limitado ao rsync. Se outro programa realizar o trabalho, tudo bem também. Acabei de tentar resolver isso usando o rsync.

Responder1

rsynctem uma opção chamada --exclude-fromoption que permite criar um arquivo contendo uma lista de todos os arquivos que você gostaria de excluir. Você pode atualizar este arquivo sempre que quiser adicionar uma nova exclusão ou remover uma antiga.

Se você criar o arquivo de exclusão no /home/user/rsync_excludenovo comando seria:

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

Ao criar o arquivo da lista de exclusões, você deve colocar cada regra de exclusão em uma linha separada. As exclusões são relativas ao seu diretório de origem. Se o seu /home/user/rsync_excludearquivo contivesse as seguintes opções:

secret_file
first_dir/subdir/*
second_dir/common_name.*
  • Qualquer arquivo ou diretório chamado secret_fileem seu diretório de origem será excluído.
  • Todos os arquivos ${source_dir}/first_dir/subdirserão excluídos, mas uma versão vazia subdirserá sincronizada.
  • Quaisquer arquivos ${source_dir}/second_dircom o prefixo common_name.serão ignorados. Então common_name.txt, common_name.jpgetc

Responder2

Já que você mencionou:Não estou limitado ao rsync:

Script para manter o espelho, permitindo adicionar arquivos extras ao alvo

Abaixo um script que faz exatamente o que você descreve.

O script pode ser executado emdetalhadomodo (a ser definido no script), que exibirá o andamento do backup (espelhamento). Não há necessidade de dizer que isso também pode ser usado para registrar os backups:

Opção detalhada

insira a descrição da imagem aqui


O conceito

1. No primeiro backup, o script:

  • cria um arquivo (no diretório de destino), onde todos os arquivos e diretórios são listados;.recentfiles
  • cria uma cópia exata (espelho) de todos os arquivos e diretórios no diretório de destino

2. No próximo backup e assim por diante

  • O script compara a estrutura de diretórios e as datas de modificação dos arquivos. Novos arquivos e diretórios na origem são copiados para o espelho. Ao mesmo tempo, um segundo arquivo (temporário) é criado, listando os arquivos e diretórios atuais no diretório de origem; .currentfiles.
  • Posteriormente, .recentfiles(listando a situação no backup anterior) é comparado com .currentfiles.Apenasos arquivos dos .recentfilesquais não estão .currentfilessão obviamente removidos da origem e serão removidos do destino.
  • Os arquivos que você adicionou manualmente à pasta de destino não são "vistos" pelo script e são deixados sozinhos.
  • Finalmente, o temporário .currentfilesé renomeado para .recentfilespara servir o próximo ciclo de backup e assim por diante.

O roteiro

#!/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)

Como usar

  1. Copie o script em um arquivo vazio e salve-o comobackup_special.py
  2. Altere -se desejar- a opção detalhada no cabeçalho do script:

    # --- choose verbose (or not)
    verbose = True
    # ---
    
  3. Execute-o com origem e destino como argumentos:

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

Velocidade

Testei o script em um diretório de 10 GB com cerca de 40.000 arquivos e diretórios em minha unidade de rede (NAS), e fiz o backup praticamente ao mesmo tempo que o rsync.

Atualizandoo diretório inteiro demorou apenas alguns segundos a mais que o rsync, em 40.000 arquivos, o que é aceitável e não é surpresa, já que o script precisa comparar o conteúdo com o último backup feito.

informação relacionada