rsync: Sincronize pastas, mas mantenha arquivos extras no destino

10

Estou começando com rsync e tentei usá-lo para manter sincronizadas duas pastas no sistema local. Eu tenho uma pasta de origem, cujo conteúdo muda ao longo do tempo (alguns arquivos são adicionados, algumas alterações e alguns são excluídos) e uma pasta de destino que eu quero quase ser um espelho da fonte. Então o que eu 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, eu gostaria de poder adicionar alguns arquivos para o alvo e não para a fonte, mas eu não quero que eles sejam deletados toda vez que eu 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 ter que alterar o comando para cada arquivo que eu quero excluir?

Atualização : devo mencionar que não estou limitado ao rsync. Se outro programa fizer o trabalho, tudo bem também. Eu apenas tentei resolver isso usando o rsync.

    
por jkrzefski 31.08.2016 / 14:28

2 respostas

9

rsync tem uma opção chamada --exclude-from , 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 em /home/user/rsync_exclude , o novo comando será:

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 arquivo /home/user/rsync_exclude continha as seguintes opções:

secret_file
first_dir/subdir/*
second_dir/common_name.*
  • Qualquer arquivo ou diretório chamado secret_file em seu diretório de origem será excluído.
  • Todos os arquivos em ${source_dir}/first_dir/subdir serão excluídos, mas uma versão vazia de subdir será sincronizada.
  • Todos os arquivos em ${source_dir}/second_dir com um prefixo de common_name. serão ignorados. Então common_name.txt , common_name.jpg etc.
por Arronical 31.08.2016 / 14:39
6

Desde que você mencionou: não estou limitado a rsync:

Script para manter o espelho, permitindo adicionar arquivos extras para segmentar

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

O script pode ser executado no modo detalhado (a ser definido no script), que exibirá o progresso do backup (espelhamento). Não é necessário dizer que isso também pode ser usado para registrar os backups:

Opção detalhada

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; %código%
  • cria uma cópia exata (espelho) de todos os arquivos e diretórios no diretório de destino

2. No backup seguinte e assim por diante

  • O script compara a estrutura de diretórios e a (s) data (s) de modificação dos arquivos. Novos arquivos e dirs 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; .recentfiles .
  • Posteriormente, .currentfiles (listando a situação no backup anterior) é comparado a .recentfiles . Somente arquivos de .currentfiles que não estão em .recentfiles são obviamente removidos da origem e serão removidos do destino.
  • Os arquivos que você adicionou manualmente à pasta de destino não aparecem de forma alguma "vistos" pelo script e são deixados em paz.
  • Finalmente, o .currentfiles temporário é renomeado para .currentfiles para atender ao próximo ciclo de backup e assim por diante.

O script

#!/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, salve-o como .recentfiles
  2. Altere-se desejar - a opção detalhada na cabeça 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

Eu testei o script em um diretório de 10 GB com cerca de 40.000 arquivos e dirs na minha unidade de rede (NAS), ele fez o backup praticamente ao mesmo tempo que o rsync.

Atualizando o diretório inteiro levou apenas alguns segundos a mais do que o rsync, em 40.000 arquivos, o que é imo aceitável e não é surpresa, já que o script precisa comparar o conteúdo ao último backup feito. / p>     

por Jacob Vlijm 31.01.2017 / 12:18