ファイルがすでに両側にあるときにディレクトリ構造を同期させる方法はありますか?

ファイルがすでに両側にあるときにディレクトリ構造を同期させる方法はありますか?

ファイルは同じですが、ディレクトリ構造が完全に異なる2つのドライブがあります。

ソース側の構造と一致するように、ターゲット側のすべてのファイルを「移動」する方法はありますか?たぶんスクリプトがありますか?

たとえば、ドライブAには次のものがあります。

/foo/bar/123.txt
/foo/bar/234.txt
/foo/bar/dir/567.txt

Bドライブには次のものがあります。

/some/other/path/123.txt
/bar/doo2/wow/234.txt
/bar/doo/567.txt

問題のファイルは大容量(800GB)なので、再コピーしたくなく、必要なディレクトリを作成してファイルを移動して構造を同期したいと思います。

私はターゲットのすべてのソースファイルを見つけて、それを一致するディレクトリに移動し、必要に応じて生成する再帰スクリプトを考えています。しかし-これは私の能力の外である!

別のエレガントなソリューションがここに提供されます。 https://superuser.com/questions/237387/any-way-to-sync-directory-struct-when-the-files-are-already-on-both-sides/238086

答え1

Gillesと一緒に行き、提案通りにUnisonを教えてください。ハサン剤。 UnisonはDropBoxより20年先のDropBoxです。多くの人(自分自身を含む)が毎日使用する堅牢なコード - 学ぶ価値が十分にあります。まだjoin入手できるすべてのプロモーションが必要です:)


これは答えの半分にすぎませんが、再び仕事に行かなければなりません:)

基本的に、私はまさにこれを行うよく知られていないjoinユーティリティを見せたいと思います。つまり、特定のフィールドで2つのテーブルを結合することです。

まず、スペースを含むファイル名を含むテストケースを設定します。

for d in a b 'c c'; do mkdir -p "old/$d"; echo $RANDOM > "old/${d}/${d}.txt"; done
cp -r old new

(一部のディレクトリおよび/またはファイル名の編集new

これで、各ディレクトリ(ハッシュ - >ファイル名)のマップを構築し、それを使用してjoin同じハッシュとファイルを一致させようとしています。地図を作成するには、次のように入力しますmakemap.sh

find "$1" -type f -exec md5 -r "{}" \; \
  | sed "s/\([a-z0-9]*\) ${1}\/\(.*\)/\1 \"\2\"/" \

makemap.sh"hash "filename""形式の行を含むファイルを生成するため、最初の列のみを結合します。

join <(./makemap.sh 'old') <(./makemap.sh 'new') >moves.txt

これにより、moves.txt次のような結果が生成されます。

49787681dd7fcc685372784915855431 "a/a.txt" "bar/a.txt"
bfdaa3e91029d31610739d552ede0c26 "c c/c c.txt" "c c/c c.txt"

次のステップは実際に作業を行うことですが、私の試みが参照に閉じ込められて役に立ちmv -iますmkdir -p

答え2

unisonというユーティリティがあります。

http://www.cis.upenn.edu/~bcpierce/unison/

ウェブサイトの説明:

UnisonはUnixとWindows用のファイル同期ツールです。これにより、ファイルとディレクトリのコレクションの2つのコピーを別のホスト(または同じホスト上の別のディスク)に保存して別々に変更し、各コピーの変更を別のコピーに伝播して更新できます。

ssh://localhost/path/to/dirUnisonは、1つ以上のルートがリモートの場合、最初の実行時に移動されたファイルのみを検出するため、ローカルファイルを同期してもルートの1つとして使用できます。

答え3

一心で使う〜のようにhasen jが提案したもの。この回答は、役に立つ可能性のあるスクリプトの例として、または既定のユーティリティのみがインストールされているサーバーに残ります。


ファイル名は階層全体で一意であると仮定します。また、ファイル名には改行文字が含まれておらず、ディレクトリツリーにはディレクトリと通常のファイルのみが含まれているとします。

  1. まず、ソースのファイル名を収集します。

    (cd /A && find . \! -type d) >A.find
    
  2. 次に、ファイルをターゲット側の適切な場所に移動します。まず、ターゲットにフラット化されたファイルツリーを作成します。古い階層へのハードリンクを維持するには、ln代わりに使用してください。mv

    mkdir /B.staging /B.new
    find /B.old -type f -exec sh -c 'mv -- "$@" "$0"' /B.staging {} +
    
  3. 一部のファイルがターゲットから欠落している可能性がある場合は、類似のフラットファイルを作成し、/A.stagingrsyncを使用してソースからターゲットにデータをコピーします。

    rsync -au /A.staging/ /B.staging/
    
  4. 次に、ファイル名を変更します。

    cd /B.new &&
    <A.find perl -l -ne '
      my $dir = '.'; s!^\./+!!;
      while (s!^([^/]+)/+!!) {  # Create directories as needed
        $dir .= "/$1";
        -d $dir or mkdir $dir or die "mkdir $dir: $!"
      }
      rename "/B.staging/$_", "$dir/$_" or die "rename -> $dir/$_: $!"
    '
    

    均等に:

    cd /B.new &&
    <A.find python -c '
    import os, sys
    for path in sys.stdin.read().splitlines():
        dir, base = path.rsplit("/", 2)
        os.rename(os.path.join("/B.new", base), path)
    '
    
  5. 最後に、ディレクトリのメタデータに興味がある場合は、すでに存在するファイルを使用してrsyncを呼び出します。

    rsync -au /A/ /B.new/
    

この記事のスニペットはテストしていません。自分の責任で使用してください。コメントでバグを報告してください。

答え4

特に継続的な同期が役に立つ場合は、調べようとします。子添付

比較的新しいです。まだ自分で使ったことがありません。

私が提案できる理由は、ファイルの2番目のコピーを保持しないからです。これは、Gitではなく一部のバージョン管理システムと同様に、ファイルを読み取り専用(「ロック」)としてマークする必要があることを意味します。

ファイルはsha256sum +ファイル拡張子(デフォルト)で識別されます。したがって、書き込みを行わずに(必要に応じて低帯域幅ネットワークを介して)、ファイルの内容は同じですが、異なるファイル名を持つ2つのストレージを同期できる必要があります。もちろん、チェックサムを実行するにはすべてのファイルを読む必要があります。

関連情報