web-dev-qa-db-ja.com

rsync:フォルダーを同期しますが、ターゲットに余分なファイルを保持します

rsyncを使い始めて、ローカルシステム上の2つのフォルダーを同期するために使用しようとしました。内容が時間とともに変化するソースフォルダー(一部のファイルが追加され、一部の変更が行われ、一部が削除されます)と、ソースのほぼミラーにしたいターゲットフォルダーがあります。だから私はこのようなrsyncを使用してみました:

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

これにより、ターゲットのコンテンツはソースのコンテンツとまったく同じになります。ただし、ソースではなくターゲットにいくつかのファイルを追加できるようにしたいのですが、rsyncを実行するたびにファイルを削除したくありません。一方、以前は同期されていたがソースで削除されたファイルは削除する必要があります。

除外するすべてのファイルのコマンドを変更せずにこれを行う方法はありますか?

更新:rsyncに限定されないことに言及する必要があります。別のプログラムが仕事を終わらせたとしても、それで問題ありません。 rsyncを使用してこれを解決しようとしました。

10
jkrzefski

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.txtcommon_name.jpgなど.
9
Arronical

あなたが言及したので:私はrsync:に限定されません

ミラーを維持するためのスクリプト。ターゲットに追加のファイルを追加できます

以下に、記述したとおりのことを行うスクリプトを示します。

スクリプトはverboseモード(スクリプトで設定される)で実行でき、バックアップの進行状況(ミラーリング)を出力します。これをバックアップのログに使用できると言う必要はありません。

詳細オプション

enter image description here


コンセプト

1.最初のバックアップで、スクリプトは次のことを行います。

  • すべてのファイルとディレクトリがリストされているファイルを(ターゲットディレクトリに)作成します。 .recentfiles
  • ターゲットディレクトリ内のすべてのファイルとディレクトリの正確なコピー(ミラー)を作成します

2.次のバックアップなど

  • スクリプトは、ファイルのディレクトリ構造と変更日を比較します。ソース内の新しいファイルとディレクトリがミラーにコピーされます。同時に、2番目の(一時)ファイルが作成され、ソースディレクトリ内の現在のファイルとディレクトリがリストされます。 .currentfiles
  • その後、.recentfiles(以前のバックアップの状況をリスト)が.currentfilesと比較されます。 Only.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. 必要に応じて、スクリプトの先頭にある詳細オプションを変更します。

    # --- choose verbose (or not)
    verbose = True
    # ---
    
  3. ソースおよびターゲットを引数として実行します。

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

速度

ネットワークドライブ(NAS)に40.000個のファイルとディレクトリがある10 GBのディレクトリでスクリプトをテストしました。rsyncとほぼ同じ時間でバックアップを作成しました。

更新40.000ファイルでは、ディレクトリ全体がrsyncよりも数秒しかかかりませんでしたが、スクリプトはコンテンツを最後に作成したバックアップと比較する必要があるため、これは受け入れられ、驚くことではありません。

6
Jacob Vlijm