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

10

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, algumas alterações e outras excluídas) 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 fonte. No entanto, gostaria de poder adicionar alguns arquivos ao destino e não ao código-fonte, mas não quero que eles sejam excluídos toda vez 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 ter que alterar o comando para todos os arquivos que eu quero excluir?

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

jkrzefski
fonte
Olá @AszunesHeart, curioso, mas testou a (s) resposta (s)?
Jacob Vlijm
Você já tentou usar a opção --delete? Essa é como a opção / MIR no Robocopy.
SDsolar

Respostas:

9

rsyncpossui uma opção chamada --exclude-fromopção que permite criar um arquivo contendo uma lista de todos os arquivos que você deseja excluir. Você pode atualizar esse arquivo sempre que desejar 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 contiver as seguintes opções:

secret_file
first_dir/subdir/*
second_dir/common_name.*
  • Qualquer arquivo ou diretório chamado secret_fileno seu diretório de origem será excluído.
  • Todos os arquivos ${source_dir}/first_dir/subdirserão excluídos, mas uma versão vazia do subdirserá sincronizada.
  • Qualquer arquivo ${source_dir}/second_dircom um prefixo de common_name.será ignorado. Então common_name.txt, common_name.jpgetc.
Arronical
fonte
Não tenho certeza se isso faz o que eu queria. Também acho impraticável listar todos os arquivos ou pastas adicionados ao destino. Eu gostaria de ter uma maneira automática de fazer isso. Digamos que tenho vários scripts no target que produzem vários arquivos de log (também no target) e não quero listar todos os locais desses arquivos no arquivo rsync_exclude-file. Existe uma maneira de fazer o rsync "lembrar" quais arquivos foram sincronizados e apenas permitir que eles sejam afetados pelo --delete?
Jkrzefski # 1/16
Desculpe, eu interpretei mal sua pergunta, embora você desejasse adicionar à fonte e não atualizar para o destino. Acho que há uma maneira de fazer o que você quer, mas vou ter que refletir um pouco. Vou comentar quando tiver tempo para editar.
1/16 arronical
@jkrzefski Se você está produzindo arquivos de outro script no destino e deseja excluí-los da fonte, por que não mudar o destino desses arquivos de log para outra pasta? Presumivelmente, se você não os está sincronizando, é porque eles são menos importantes.
6

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

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

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

O script pode ser executado no modo detalhado (a ser definido no script), o que produzirá o progresso do backup (espelhamento). Não é necessário 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 backup seguinte 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 fonte 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 a .currentfiles. Somente arquivos dos .recentfilesquais não estão.currentfiles estão são obviamente removidos da fonte 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 em paz.
  • Por fim, o temporário .currentfilesé renomeado .recentfilespara servir o 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 backup_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>
    

Rapidez

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

A atualização do diretório inteiro levou apenas alguns segundos a mais do que o rsync, em 40.000 arquivos, o que é quase aceitável e não surpreende, pois o script precisa comparar o conteúdo com o último backup feito.

Jacob Vlijm
fonte
Hi @ Aszune'sHeart adicionou uma opção de script. Mencione se tudo está claro.
Jacob Vlijm 31/01