rsync: ซิงค์โฟลเดอร์ แต่เก็บไฟล์ไว้ในเป้าหมาย


10

ฉันเริ่มต้นด้วยrsyncและพยายามที่จะใช้เพื่อให้สองโฟลเดอร์ในระบบท้องถิ่นซิงค์ ฉันมีโฟลเดอร์ต้นทางซึ่งมีการเปลี่ยนแปลงเนื้อหาเมื่อเวลาผ่านไป (เพิ่มไฟล์บางไฟล์การเปลี่ยนแปลงบางอย่างและลบไปแล้ว) และโฟลเดอร์เป้าหมายที่ฉันต้องการเป็นมิเรอร์ของแหล่งที่มา ดังนั้นสิ่งที่ฉันลองใช้ rsync เช่นนี้:

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

สิ่งนี้จะทำให้เนื้อหาของเป้าหมายเหมือนกันกับเนื้อหาของแหล่งที่มา อย่างไรก็ตามฉันต้องการเพิ่มไฟล์บางไฟล์ไปยังเป้าหมายและไม่ไปยังแหล่งที่มา แต่ฉันไม่ต้องการให้ลบทุกครั้งที่ฉันทำ rsync ในทางกลับกันไฟล์ที่เคยซิงค์และถูกลบในแหล่งที่มาควรจะยังคงถูกลบ

มีวิธีการทำเช่นนี้โดยไม่ต้องเปลี่ยนคำสั่งสำหรับทุกไฟล์ที่ฉันต้องการยกเว้นหรือไม่?

อัปเดต : ฉันควรระบุว่าไม่ จำกัด rsync ถ้าโปรแกรมอื่นทำให้งานเสร็จสมบูรณ์ ฉันแค่พยายามแก้ปัญหานี้โดยใช้ rsync


สวัสดี @ AszunesHeart อยากรู้อยากเห็น แต่คุณทดสอบคำตอบแล้วหรือยัง
Jacob Vlijm

คุณได้ลองใช้ตัวเลือก --delete แล้วหรือยัง? อันนั้นก็เหมือนตัวเลือก / MIR ใน Robocopy
SDsolar

คำตอบ:


9

rsyncมีตัวเลือกที่เรียกว่า--exclude-fromตัวเลือกที่ช่วยให้คุณสร้างไฟล์ที่มีรายการไฟล์ที่คุณต้องการแยกออก คุณสามารถอัปเดตไฟล์นี้เมื่อใดก็ตามที่คุณต้องการเพิ่มการยกเว้นใหม่หรือลบไฟล์เก่า

หากคุณสร้างไฟล์แยกที่/home/user/rsync_excludeคำสั่งใหม่จะเป็น:

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

เมื่อสร้างไฟล์รายการแยกคุณควรวางกฎการแยกแต่ละรายการในบรรทัดแยกต่างหาก การยกเว้นนั้นสัมพันธ์กับไดเรกทอรีต้นทางของคุณ หาก/home/user/rsync_excludeไฟล์ของคุณมีตัวเลือกต่อไปนี้:

secret_file
first_dir/subdir/*
second_dir/common_name.*
  • ไฟล์หรือไดเรกทอรีใด ๆ ที่เรียกว่าsecret_fileในไดเรกทอรีต้นทางของคุณจะถูกยกเว้น
  • ไฟล์ใด ๆ ใน${source_dir}/first_dir/subdirจะถูกแยกออก แต่subdirจะซิงค์เวอร์ชันที่ว่างเปล่า
  • ไฟล์ใด ๆ ที่${source_dir}/second_dirมีส่วนนำหน้าของcommon_name.จะถูกละเว้น ดังนั้นcommon_name.txt, common_name.jpgฯลฯ

ฉันไม่แน่ใจว่านี่เป็นสิ่งที่ฉันต้องการหรือไม่ นอกจากนี้ฉันพบว่ามันเป็นไปไม่ได้ที่จะแสดงรายการทุกไฟล์หรือโฟลเดอร์ที่เพิ่มเข้าไปในเป้าหมาย ฉันอยากจะมีวิธีอัตโนมัติในการทำเช่นนั้น สมมติว่าฉันมีสคริปต์ต่าง ๆ ในเป้าหมายที่สร้างไฟล์บันทึกหลายไฟล์ (รวมถึงเป้าหมาย) และฉันไม่ต้องการแสดงรายการทุกตำแหน่งของไฟล์เหล่านั้นใน rsync_exclude-file มีวิธีใดที่จะทำให้ rsync "จดจำ" ไฟล์ที่ซิงค์และปล่อยเฉพาะไฟล์ที่ได้รับผลกระทบจาก - ลบ?
jkrzefski

ขออภัยฉันอ่านคำถามของคุณผิดฉันคิดว่าคุณต้องการที่จะเพิ่มแหล่งที่มาและมีสิ่งที่ไม่อัปเดตไปยังเป้าหมาย ฉันคิดว่ามีวิธีที่จะทำสิ่งที่คุณต้องการ แต่ฉันจะต้องไตร่ตรองให้ดี ฉันจะแสดงความคิดเห็นเมื่อฉันมีเวลาแก้ไข
Arronical

@jkrzefski หากคุณสร้างไฟล์จากสคริปต์อื่นในเป้าหมายและต้องการแยกไฟล์ออกจากแหล่งที่มาทำไมไม่เปลี่ยนปลายทางของไฟล์บันทึกเหล่านั้นไปยังโฟลเดอร์อื่น? สันนิษฐานว่าหากคุณไม่ได้ซิงค์พวกเขาเป็นเพราะพวกเขามีความสำคัญน้อยกว่า

6

ตั้งแต่ที่คุณพูดถึง: ฉันไม่ จำกัด rsync:

สคริปต์เพื่อรักษามิเรอร์อนุญาตให้เพิ่มไฟล์พิเศษในการกำหนดเป้าหมาย

ด้านล่างสคริปต์ที่ทำสิ่งที่คุณอธิบาย

สคริปต์สามารถเรียกใช้ในโหมดverbose (จะตั้งค่าในสคริปต์) ซึ่งจะส่งออกความคืบหน้าของการสำรองข้อมูล (การทำมิเรอร์) ไม่จำเป็นต้องพูดว่าสามารถใช้บันทึกการสำรองข้อมูลได้:

ตัวเลือก verbose

ป้อนคำอธิบายรูปภาพที่นี่


แนวคิด

1. ในการสำรองข้อมูลครั้งแรกสคริปต์:

  • สร้างไฟล์ (ในไดเรกทอรีเป้าหมาย) ซึ่งมีการระบุไฟล์และไดเรกทอรีทั้งหมด .recentfiles
  • สร้างสำเนาที่แน่นอน (มิเรอร์) ของไฟล์และไดเรกทอรีทั้งหมดในไดเรกทอรีเป้าหมาย

2. ในการสำรองข้อมูลครั้งต่อไป

  • สคริปต์จะเปรียบเทียบโครงสร้างไดเรกทอรีและวันที่แก้ไขไฟล์ ไฟล์ใหม่และ dirs ในแหล่งที่มาจะถูกคัดลอกไปยังมิเรอร์ ในเวลาเดียวกันไฟล์ที่สอง (ชั่วคราว) จะถูกสร้างรายการของไฟล์ปัจจุบันและ dirs ในไดเรกทอรีแหล่ง; .currentfiles.
  • ต่อจากนั้น.recentfiles(รายสถานการณ์ในการสำรองข้อมูลก่อนหน้านี้) .currentfilesเมื่อเทียบกับ เฉพาะไฟล์.recentfilesที่ไม่ได้อยู่ใน.currentfilesนั้นจะถูกลบออกอย่างชัดเจนจากแหล่งที่มาและจะถูกลบออกจากเป้าหมาย
  • ไฟล์ที่คุณเพิ่มด้วยตนเองไปยังโฟลเดอร์เป้าหมายไม่ได้อยู่ในสคริปต์ "เห็น" แล้วและถูกทิ้งไว้ตามลำพัง
  • ในที่สุดการ.currentfilesเปลี่ยนชั่วคราวจะ.recentfilesให้บริการรอบการสำรองต่อไปและอื่น ๆ

สคริปต์

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

วิธีใช้

  1. คัดลอกสคริปต์ลงในไฟล์เปล่าบันทึกเป็น backup_special.py
  2. เปลี่ยนถ้าคุณต้องการ - ตัวเลือก verbose ในส่วนหัวของสคริปต์:

    # --- choose verbose (or not)
    verbose = True
    # ---
    
  3. รันด้วยซอร์สและเป้าหมายเป็นอาร์กิวเมนต์:

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

ความเร็ว

ฉันทดสอบสคริปต์ในไดเรกทอรี 10 GB โดยมีไฟล์และไดร์เวอร์ถึง 40,000 ไฟล์ในไดรฟ์เครือข่าย (NAS) ของฉันมันทำให้การสำรองข้อมูลในเวลาเดียวกับ rsync

การอัปเดตไดเรกทอรีทั้งหมดใช้เวลามากกว่า rsync เพียงไม่กี่วินาทีในไฟล์ 40.000 ซึ่งเป็นที่ยอมรับ imo และไม่แปลกใจเนื่องจากสคริปต์ต้องเปรียบเทียบเนื้อหากับการสำรองข้อมูลที่ทำล่าสุด


สวัสดี @ Aszune'sHeart เพิ่มตัวเลือกสคริปต์ กรุณาพูดถึงถ้าทุกอย่างชัดเจน
Jacob Vlijm
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.