rsync: Sinkronkan folder, tetapi simpan file tambahan di target

10

Saya memulai rsyncdan mencoba menggunakannya untuk menyinkronkan dua folder pada sistem lokal. Saya memiliki folder sumber, yang isinya berubah dari waktu ke waktu (beberapa file ditambahkan, beberapa perubahan dan beberapa dihapus) dan folder target yang saya ingin hampir menjadi cermin dari sumber. Jadi yang saya coba adalah menggunakan rsync seperti ini:

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

Ini menjaga isi target sama persis dengan isi sumber. Namun, saya ingin dapat menambahkan beberapa file ke target dan bukan ke sumber, tapi saya tidak ingin mereka dihapus setiap kali saya melakukan rsync. Di sisi lain, file yang digunakan untuk disinkronkan dan kemudian dihapus dalam sumber harus tetap dihapus.

Apakah ada cara untuk melakukan ini tanpa harus mengubah perintah untuk setiap file yang ingin saya kecualikan?

Pembaruan : Saya harus menyebutkan bahwa saya tidak terbatas pada rsync. Jika program lain menyelesaikan pekerjaan, itu juga baik-baik saja. Saya baru saja mencoba menyelesaikan ini menggunakan rsync.

jkrzefski
sumber
Hai @ AszunesHeart, hanya ingin tahu, tetapi apakah Anda menguji jawabannya?
Jacob Vlijm
Sudahkah Anda mencoba mengambil opsi --delete? Yang itu seperti opsi / MIR di Robocopy.
SDsolar

Jawaban:

9

rsyncmemiliki opsi bernama --exclude-fromopsi yang memungkinkan Anda membuat file yang berisi daftar file apa pun yang ingin Anda kecualikan. Anda dapat memperbarui file ini kapan pun Anda ingin menambahkan pengecualian baru, atau menghapus yang lama.

Jika Anda membuat file kecualikan pada /home/user/rsync_excludeperintah baru adalah:

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

Saat membuat file daftar pengecualian, Anda harus meletakkan setiap aturan pengecualian pada baris terpisah. Pengecualian relatif terhadap direktori sumber Anda. Jika /home/user/rsync_excludefile Anda berisi opsi berikut:

secret_file
first_dir/subdir/*
second_dir/common_name.*
  • File atau direktori apa pun yang dipanggil secret_filedalam direktori sumber Anda akan dikecualikan.
  • File apa pun di ${source_dir}/first_dir/subdirakan dikecualikan, tetapi versi kosong subdirakan disinkronkan.
  • File apa pun ${source_dir}/second_dirdengan awalan common_name.akan diabaikan. Jadi common_name.txt, common_name.jpgdll
Arronikal
sumber
Saya tidak yakin apakah ini yang saya inginkan. Saya juga merasa tidak praktis untuk mendaftar setiap file atau folder yang ditambahkan ke target. Saya lebih suka memiliki cara otomatis untuk melakukan itu. Katakanlah saya memiliki berbagai skrip di target yang menghasilkan banyak file log (juga di target) dan saya tidak ingin mencantumkan setiap lokasi file tersebut di file rsync_exclude. Apakah ada cara untuk membuat rsync "mengingat" file mana yang disinkronkan dan hanya membiarkannya dipengaruhi oleh --delete?
jkrzefski
Maaf, saya salah membaca pertanyaan Anda, saya pikir Anda ingin menambahkan ke sumber, dan minta mereka tidak memperbarui ke target. Saya pikir ada cara untuk melakukan apa yang Anda inginkan, tetapi saya harus merenungkannya sebentar. Saya akan berkomentar begitu saya punya waktu untuk mengedit.
Arronical
@ jkrzefski Jika Anda membuat file dari skrip lain di target, dan ingin mengecualikannya dari sumber, lalu mengapa tidak mengubah tujuan file log tersebut ke folder lain? Agaknya, jika Anda tidak menyinkronkannya, itu karena mereka kurang penting.
6

Karena Anda menyebutkan: Saya tidak terbatas pada rsync:

Script untuk mempertahankan mirror, memungkinkan untuk menambahkan file tambahan ke target

Di bawah ini skrip yang melakukan apa yang Anda gambarkan

Skrip dapat dijalankan dalam mode verbose (diatur dalam skrip), yang akan menampilkan progres cadangan (mirroring). Tidak perlu mengatakan ini juga dapat digunakan untuk mencatat cadangan:

Opsi verbose

masukkan deskripsi gambar di sini


Konsep

1. Pada cadangan pertama, skrip:

  • membuat file (dalam direktori target), di mana semua file dan direktori terdaftar; .recentfiles
  • membuat salinan persis (mirror) dari semua file dan direktori di direktori target

2. Pada cadangan berikutnya dan seterusnya

  • Skrip membandingkan struktur direktori dan tanggal modifikasi file. File dan dirs baru di sumber disalin ke cermin. Pada saat yang sama file kedua (sementara) dibuat, daftar file saat ini dan direktori dalam direktori sumber; .currentfiles.
  • Selanjutnya, .recentfiles(daftar situasi pada cadangan sebelumnya) dibandingkan dengan .currentfiles. Hanya file .recentfilesyang tidak ada di dalamnya yang .currentfilesjelas dihapus dari sumbernya, dan akan dihapus dari target.
  • File yang Anda tambahkan secara manual ke folder target tidak "dilihat" oleh skrip, dan dibiarkan sendiri.
  • Akhirnya, sementara .currentfilesdiganti namanya .recentfilesuntuk melayani siklus cadangan berikutnya dan seterusnya.

Naskah

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

Cara Penggunaan

  1. Salin skrip ke file kosong, simpan sebagai backup_special.py
  2. Ubah -jika Anda mau- opsi verbose di bagian atas skrip:

    # --- choose verbose (or not)
    verbose = True
    # ---
    
  3. Jalankan dengan sumber dan target sebagai argumen:

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

Kecepatan

Saya menguji script pada direktori 10 GB dengan sekitar 40.000 file dan direktori pada drive jaringan saya (NAS), itu membuat cadangan dalam waktu yang hampir bersamaan dengan rsync.

Memperbarui seluruh direktori hanya membutuhkan beberapa detik lebih dari rsync, pada 40.000 file, yang dapat diterima dan tidak mengejutkan, karena skrip perlu membandingkan konten dengan cadangan yang terakhir dibuat.

Yakub Vlijm
sumber
Hai @ Aszune'sHeart menambahkan opsi skrip. Harap sebutkan jika semuanya jelas.
Jacob Vlijm