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
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 .recentfiles
quais 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 .recentfiles
para 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
- Copie o script em um arquivo vazio, 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>
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.