rsync: sincroniza carpetas, pero mantiene archivos adicionales en el destino

10

Estoy comenzando rsynce intenté usarlo para mantener sincronizadas dos carpetas en el sistema local. Tengo una carpeta de origen, cuyo contenido cambia con el tiempo (se agregan algunos archivos, algunos cambios y otros se eliminan) y una carpeta de destino que quiero que sea casi un espejo de la fuente. Entonces, lo que probé fue usar rsync así:

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

Esto mantiene el contenido del objetivo exactamente igual que el contenido de la fuente. Sin embargo, me gustaría poder agregar algunos archivos al destino y no a la fuente, pero no quiero que se eliminen cada vez que hago rsync. Por otro lado, los archivos que solían sincronizarse y luego se eliminaron en la fuente aún deberían eliminarse.

¿Hay alguna manera de hacer esto sin tener que alterar el comando para cada archivo que quiero excluir?

Actualización : debo mencionar que no estoy limitado a rsync. Si otro programa hace el trabajo, también está bien. Solo intenté resolver esto usando rsync.

jkrzefski
fuente
Hola @AszunesHeart, solo curiosidad, pero ¿probaste la (s) respuesta (s)?
Jacob Vlijm
¿Has intentado eliminar la opción --delete? Esa es como la opción / MIR en Robocopy.
SDsolar

Respuestas:

9

rsynctiene una opción llamada --exclude-fromopción que le permite crear un archivo que contiene una lista de los archivos que desea excluir. Puede actualizar este archivo siempre que desee agregar una nueva exclusión o eliminar una antigua.

Si crea el archivo de exclusión en /home/user/rsync_excludeel nuevo comando sería:

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

Al crear el archivo de lista de exclusión, debe colocar cada regla de exclusión en una línea separada. Las exclusiones son relativas a su directorio de origen. Si su /home/user/rsync_excludearchivo contenía las siguientes opciones:

secret_file
first_dir/subdir/*
second_dir/common_name.*
  • Cualquier archivo o directorio llamado secret_fileen su directorio fuente será excluido.
  • Se ${source_dir}/first_dir/subdirexcluirán todos los archivos , pero subdirse sincronizará una versión vacía de .
  • Cualquier archivo ${source_dir}/second_dircon un prefijo common_name.será ignorado. Entonces common_name.txt, common_name.jpgetc.
Arronico
fuente
No estoy seguro si esto hace lo que quería. También me parece poco práctico enumerar cada archivo o carpeta que se agrega al destino. Prefiero tener una forma automática de hacerlo. Digamos que tengo varios scripts en target que producen múltiples archivos de registro (también en target) y no quiero enumerar cada ubicación de esos archivos en el archivo rsync_exclude. ¿Hay alguna manera de hacer que rsync "recuerde" qué archivos se sincronizaron y solo dejar que esos se vean afectados por --delete?
jkrzefski
Lo sentimos, leí mal su pregunta, pensé que deseaba agregarla a la fuente y hacer que no se actualicen a la orientación. Creo que hay una manera de hacer lo que quieres, pero tendré que reflexionar un poco. Comentaré una vez que haya tenido tiempo de editar.
Arronico
@jkrzefski Si está produciendo archivos de otra secuencia de comandos en destino y desea excluirlos de la fuente, ¿por qué no cambiar el destino de esos archivos de registro a otra carpeta? Presumiblemente, si no los está sincronizando, es porque son menos importantes.
6

Como mencionó: no estoy limitado a rsync:

Script para mantener el espejo, lo que permite agregar archivos adicionales al destino

Debajo de un script que hace exactamente lo que usted describe.

La secuencia de comandos se puede ejecutar en modo detallado (que se establecerá en la secuencia de comandos), lo que generará el progreso de la copia de seguridad (reflejo). No es necesario decir que esto también se puede usar para registrar las copias de seguridad:

Opción detallada

ingrese la descripción de la imagen aquí


El concepto

1. En la primera copia de seguridad, el script:

  • crea un archivo (en el directorio de destino), donde se enumeran todos los archivos y directorios; .recentfiles
  • crea una copia exacta (espejo) de todos los archivos y directorios en el directorio de destino

2. En el siguiente y así sucesivamente copia de seguridad

  • El script compara la estructura del directorio y las fechas de modificación de los archivos. Los nuevos archivos y directorios en la fuente se copian en el espejo. Al mismo tiempo, se crea un segundo archivo (temporal), que enumera los archivos y directorios actuales en el directorio de origen; .currentfiles.
  • Posteriormente, .recentfilesse compara (enumerando la situación en la copia de seguridad anterior) .currentfiles. Obviamente, solo los archivos de los .recentfilesque no .currentfilesestán se eliminan del origen y se eliminarán del destino.
  • Los archivos que agregó manualmente a la carpeta de destino no son "vistos" de ninguna manera por el script, y se dejan solos.
  • Finalmente, el nombre temporal .currentfilesse renombra para .recentfilesservir el próximo ciclo de copia de seguridad y así sucesivamente.

La secuencia de comandos

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

Cómo utilizar

  1. Copie el script en un archivo vacío, guárdelo como backup_special.py
  2. Cambie, si lo desea, la opción detallada en el encabezado del script:

    # --- choose verbose (or not)
    verbose = True
    # ---
    
  3. Ejecútelo con fuente y destino como argumentos:

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

Velocidad

Probé el script en un directorio de 10 GB con unos 40,000 archivos y directorios en mi unidad de red (NAS), hizo la copia de seguridad casi al mismo tiempo que rsync.

La actualización de todo el directorio tomó solo unos segundos más que rsync, en 40,000 archivos, lo cual es bastante aceptable y no sorprende, ya que el script necesita comparar el contenido con la última copia de seguridad realizada.

Jacob Vlijm
fuente
Hola @ Aszune'sHeart agregó una opción con guión. Mencione si todo está claro.
Jacob Vlijm el