Estou começando rsync
e 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
rsync
tem uma opção chamada --exclude-from
option 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_exclude
novo 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_exclude
arquivo contivesse 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
${source_dir}/first_dir/subdir
serão excluídos, mas uma versão vaziasubdir
será sincronizada. - Quaisquer arquivos
${source_dir}/second_dir
com o prefixocommon_name.
serão ignorados. Entãocommon_name.txt
,common_name.jpg
etc
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
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.recentfiles
quais não estão.currentfiles
sã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.recentfiles
para 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
- Copie o script em um arquivo vazio e salve-o como
backup_special.py
Altere -se desejar- a opção detalhada no cabeçalho do script:
# --- choose verbose (or not) verbose = True # ---
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.