rsync: sincronizza le cartelle, ma mantieni i file extra nella destinazione


10

Sto iniziando con rsynce ho provato a usarlo per mantenere sincronizzate due cartelle sul sistema locale. Ho una cartella di origine, il cui contenuto cambia nel tempo (alcuni file vengono aggiunti, alcune modifiche e alcuni eliminati) e una cartella di destinazione che voglio quasi essere un mirror della fonte. Quindi quello che ho provato è stato usare rsync in questo modo:

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

Ciò mantiene il contenuto del target esattamente uguale al contenuto del sorgente. Tuttavia, vorrei poter aggiungere alcuni file al target e non al sorgente, ma non voglio che vengano eliminati ogni volta che eseguo rsync. D'altra parte, i file che prima erano sincronizzati e poi cancellati nel sorgente dovrebbero essere comunque cancellati.

C'è un modo per farlo senza dover modificare il comando per ogni file che voglio escluso?

Aggiornamento : dovrei menzionare che non sono limitato a rsync. Se un altro programma esegue il lavoro, va bene lo stesso. Ho appena provato a risolverlo usando rsync.


Ciao @AszunesHeart, solo curioso, ma hai provato la risposta (s)?
Jacob Vlijm,

Hai provato a togliere l'opzione --delete? Quello è come l'opzione / MIR in Robocopy.
SDsolar,

Risposte:


9

rsyncha un'opzione chiamata --exclude-fromopzione che consente di creare un file contenente un elenco di tutti i file che si desidera escludere. Puoi aggiornare questo file ogni volta che vuoi aggiungere una nuova esclusione o rimuoverne una vecchia.

Se si crea il file di esclusione con /home/user/rsync_excludeil nuovo comando sarebbe:

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

Quando si crea il file dell'elenco di esclusione, è necessario inserire ciascuna regola di esclusione su una riga separata. Le esclusioni sono relative alla directory di origine. Se il tuo /home/user/rsync_excludefile conteneva le seguenti opzioni:

secret_file
first_dir/subdir/*
second_dir/common_name.*
  • Qualsiasi file o directory chiamato secret_filenella directory di origine verrà escluso.
  • Tutti i file in ${source_dir}/first_dir/subdirverranno esclusi, ma subdirverrà sincronizzata una versione vuota di .
  • Tutti i file ${source_dir}/second_dircon un prefisso di common_name.verranno ignorati. Così common_name.txt, common_name.jpgecc

Non sono sicuro se questo fa quello che volevo. Inoltre trovo poco pratico elencare ogni file o cartella che viene aggiunto alla destinazione. Preferirei avere un modo automatico per farlo. Diciamo che ho diversi script nella destinazione che producono più file di registro (anche nella destinazione) e non voglio elencare ogni posizione di tali file nel file rsync_exclude. C'è un modo per far sì che rsync "ricordi" quali file sono stati sincronizzati e che solo --delete ha influenzato quelli?
jkrzefski,

Siamo spiacenti, ho letto male la tua domanda, ho pensato che volessi aggiungere alla fonte e quelli che non si aggiornano alla destinazione. Penso che ci sia un modo per fare quello che vuoi, ma dovrò rifletterci un po '. Commenterò una volta che avrò avuto il tempo di modificare.
Arronical

@jkrzefski Se si stanno producendo file da un altro script nella destinazione e si desidera escluderli dall'origine, perché non modificare la destinazione di tali file di registro in un'altra cartella? Presumibilmente, se non li sincronizzi, è perché sono meno importanti.

6

Da quando hai menzionato: non sono limitato a rsync:

Script per mantenere il mirror, consentendo di aggiungere file extra alla destinazione

Di seguito uno script che fa esattamente quello che descrivi.

Lo script può essere eseguito in modalità dettagliata (da impostare nello script), che genererà l'avanzamento del backup (mirroring). Non c'è bisogno di dire che può essere utilizzato anche per registrare i backup:

Opzione dettagliata

inserisci qui la descrizione dell'immagine


Il concetto

1. Al primo backup, lo script:

  • crea un file (nella directory di destinazione), in cui sono elencati tutti i file e le directory; .recentfiles
  • crea una copia esatta (mirror) di tutti i file e le directory nella directory di destinazione

2. Al successivo e così via backup

  • Lo script confronta la struttura delle directory e le date di modifica dei file. I nuovi file e directory nell'origine vengono copiati nel mirror. Allo stesso tempo viene creato un secondo file (temporaneo), che elenca i file e le directory correnti nella directory di origine; .currentfiles.
  • Successivamente, .recentfiles(elencando la situazione sul backup precedente) viene confrontato .currentfiles. Solo i file da .recentfilescui non sono presenti .currentfilesvengono ovviamente rimossi dall'origine e verranno rimossi dalla destinazione.
  • I file aggiunti manualmente alla cartella di destinazione non vengono comunque "visti" dallo script e vengono lasciati soli.
  • Infine, il temporaneo .currentfilesviene rinominato per .recentfilesservire il ciclo di backup successivo e così via.

Il copione

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

Come usare

  1. Copia lo script in un file vuoto, salvalo come backup_special.py
  2. Cambia -se vuoi- l'opzione dettagliata nell'intestazione dello script:

    # --- choose verbose (or not)
    verbose = True
    # ---
    
  3. Eseguilo con source e target come argomenti:

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

Velocità

Ho testato lo script su una directory da 10 GB con circa 40.000 file e directory sul mio drive di rete (NAS), ha fatto il backup praticamente nello stesso momento di rsync.

L'aggiornamento dell'intera directory ha richiesto solo pochi secondi in più rispetto a rsync, su 40.000 file, il che è inaccettabile e nessuna sorpresa, poiché lo script deve confrontare il contenuto con l'ultimo backup eseguito.


Ciao @ Aszune'sHeart ha aggiunto un'opzione con script. Si prega di menzionare se tutto è chiaro.
Jacob Vlijm,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.