変更されたディレクトリツリーでファイルを「検索して置換する」 Bash コマンド

変更されたディレクトリツリーでファイルを「検索して置換する」 Bash コマンド

私のサーバーの中央リポジトリに研究用ファイルのコレクションを構築しました。私は常にリポジトリを管理し、ディレクトリの名前を変更して移動し、ディレクトリのツリー構造を変更します。私はリポジトリ内のファイルのローカルコピーを常に更新して編集しています。これにより、リポジトリにクライアントの新しいファイルに置き換える必要があるファイルがありますが、ディレクトリツリーが変更される状況が発生します。

rsyncまだ学んでいないオプションはありますか?ディレクトリツリーを変更すると、rsyncが機能しないことがわかりました。私はテストしましたrsync --existing

それとも少しありますか?コマンドライン-fu使用findしてmvWebなどを検索してみてください。リソース検索のみを検索して置き換えるファイルにはい。

- アップデート1可能であれば、@meuhと@Lqueryvgに素晴らしい答えを提供します。 @css1971が彼の答えに追加され、ファイル管理を転覆させる強力なケースを作成したことを確認しました。残念ながら、時間が足りなくなり、報酬システムが私のお気に入りの答えを選びました。私はすべての答えが非常に有益であることがわかりました。みんなに感謝したいです。次の週末に、より思慮深い意見を提示できることを楽しみにしています。

答え1

[私のコメントからOPに拡張]。flatすべてのファイルのコピーを含みますが、単純なリストにあるリポジトリにディレクトリを作成します。コピーはハードリンクなので、スペースを取らないでください。ローカルコンピュータでも同じことをしてください。その後、ローカルフラットディレクトリからリモートフラットディレクトリにrsyncできます。 rsyncを使用すると、リモートハードリンクが保存されるため、すべてのリモートファイルが更新されます--inplace。フラットディレクトリはrsyncの前にスクリプトとして作成され、rsyncの後に削除できます。

以下は、実行可能な概念証明テストスクリプトです。

#!/bin/bash
dosync(){  # function to create flat trees and rsync them
    mkdir flat
    mkdir flatl
    find repo  -type f -exec ln {} flat \;
    find local -type f -exec ln {} flatl \;
    rsync -aHv --inplace flatl/ flat
    rm -fr flat flatl
}

# create repo and local dirs with same content. 3 empty files
cd /tmp || exit
mkdir repo
( cd repo; touch a b c )
mkdir local
rsync -a repo/ local

dosync
echo hi >local/a   # example change of local file a
dosync
mkdir repo/new     # example move of repo file b
mv repo/b repo/new/
echo hello >local/b  # change local file b
dosync

ls -lR repo local
# rm -fr flat flatl repo local

反対方向の場合、dosyncがローカル修正をリポジトリに転送した後、単にrm -fr local「rsync -a repo / local」を使用してリポジトリ全体をローカルファイルシステムにコピーできます。代わりに、同様の技術を使用してリポジトリに転送する必要がある新しいファイルの数を減らすことができます。

reversesync(){
    mkdir flat
    mkdir flatl
    find repo  -type f -exec ln {} flat \;
    find local -type f -exec ln {} flatl \;
    mv flat repo/flat
    mv flatl local/flat # not flatl!
    rsync -aHv --delete repo/ local
    rm -fr repo/flat local/flat
}

これにより、rsyncがハードリンクされたファイルを表示してコピーを回避できるように、フラット化されたツリーはそれぞれストレージとローカルディレクトリに移動されます。 (明らかに今回はフラットディレクトリの名前は同じでなければなりません)。


既知のファイルが変更されている場合は、リポジトリでfindそれを使用してツリーから新しい場所を取得し、そこからファイルを同期できます。たとえば、

file=mychangedfile.abc
to=$(find repo -name "$file")
from=$(find local -name "$file")
rsync -av "$from" "$to"

これはリポジトリがインストールされていると仮定しますssh repo find...。そうでなければ、リポジトリでsshを使用できない場合は、rsyncを使用して仮想ローカルターゲットにファイルのリストを取得し、目的のファイルを抽出できます。

to=$(rsync -a --list-only repo dummy | awk '/\/'"$file"'$/{print $NF}')

答え2

私はrsyncがfindとmvのように間違ったツールだと思います。私の提案は、ソフトウェア構成管理システムを使用することです。これには、Subversion、Git、Mercurial、Bazaarなどが含まれます。どちらもツリー構造の変更を簡単に処理できます。

説明する構造には、クライアントシステムにツリー構造Aがあり、セカンダリロケーション(ローカルでもリモートでも)にストレージツリー構造Bがあります。

両方を更新した場合は、競合する変更を適用する必要があります。正しい順序で両方のリポジトリを一貫して維持します。順番に適用されないと、構造が存在しなくなるため、ある構造への変更を他の構造に直接適用できない状況が発生します。

現時点では、両方のリポジトリを一貫して作成するためにどの変更を適用する必要があるのか​​を自動的に知ることができるrsyncオプションはありません。それはそれのために設計されていません。あるリポジトリが他のリポジトリと同じように見えるようにすることはできますが、一度に片側だけを変更するだけです。たとえば、A、B、Aを交互に変更します。いつでもファブリックAまたはBのいずれかをマスターとして指定し、変更を同期する必要があります。一方向のみ一度。

私はまた、あなたが探している結果を達成する単純なcommandline-fuコマンドがあると信じていないので、今あなたはシェルプログラミング領域にいます。

あなただけが変わったらツリー構造Bとのみ文書内容Aが変更されたファイルのファイル名を見つけ、Bからそのファイルの新しいパスを取得し、一致するようにAのツリー構造を変更するのは比較的簡単です。これただファイル名が一意の場合は有効です。

Aの構造をBと一致させる擬似コードは次のとおりです。

generate list of file names in A and their paths

For each of the names in A
    find that same name in B
    If the path of A is the same as B 
        continue to the next file
    if not then
        create the directory structure in A
        move the file to the new location.
    if the old path in A is now empty
        delete the directory.
        repeat 
            check if the parent directory is now empty, then delete it.
        until a non empty directory

ツリー構造が同期されると、AはBのまったく同じパスに直接コピーできます。 rsyncの--updateオプションを使用すると、古いファイルを新しいファイルで双方向に上書きできます。

ファイル名セレクタとして find を使用してローカルに変更されたファイルを既存のリポジトリにコピーするいくつかのサンプルシェルコード。

#!/bin/bash

set -xv

localRepo=/tmp/a
remoteRepo=/tmp/b

rm -rf $localRepo $remoteRepo

mkdir -p $localRepo/1/2/ $localRepo/1/3/
mkdir -p $remoteRepo/2/1/ $remoteRepo/3/1/

echo a12 > $localRepo/1/2/file
echo b21 > $remoteRepo/2/1/file

echo a13 > $localRepo/1/3/file1
echo b31 > $remoteRepo/3/1/file1

echo ex1
cat $localRepo/1/2/file $remoteRepo/2/1/file
echo ex2
cat $localRepo/1/3/file1 $remoteRepo/3/1/file1



localFileNameList=$(find $localRepo -type f -mtime -1 | xargs -L 1 basename)


for localFileName in $localFileNameList
do
    localFilePath=$(find $localRepo -name $localFileName | xargs dirname)
    backFile=$(find $remoteRepo -name $localFileName)
    repoDir=$(dirname $backFile)

    cp $localFilePath/$localFileName $repoDir

done

echo ex1
cat $localRepo/1/2/file $remoteRepo/2/1/file
echo ex2
cat $localRepo/1/3/file1 $remoteRepo/3/1/file1

たとえば、使いやすいSCMの1つであるSubversionにファイルシステムをインポートするには、次の手順を実行します。

e.g.
mkdir /tmp/svn
svnadmin create /tmp/svn/reponame

cd /tmp/b
svn import -m "The initial import " file:///tmp/svn/reponame
Adding         2
Adding         2/1
Adding         2/1/file
Adding         3
Adding         3/1
Adding         3/1/file1

その後、リポジトリを確認してローカルを変更します。

$ cd /tmp
$ svn checkout file:///tmp/svn/reponame 
A    reponame/2
A    reponame/2/1
A    reponame/2/1/file
A    reponame/3
A    reponame/3/1
A    reponame/3/1/file1
Checked out revision 1.
/tmp:

$ cd reponame/
/tmp/reponame:
$ ls -ltr
total 8
drwxrwxr-x 3 css1971 css1971 4096 Apr 11 12:04 3
drwxrwxr-x 3 css1971 css1971 4096 Apr 11 12:04 2
/tmp/reponame:
$ svn move 3 4
A         4
D         3
D         3/1
D         3/1/file1
/tmp/reponame:

変更をリポジトリに再コミットします。

$ svn commit -m "renamed dir"
Deleting       3
Adding         4

Committed revision 2.

この時点から、svnツールを使用して一般的なワークフローの一部としてリポジトリを操作します。

便利なコマンド:

svn import
svn update
svn commit
svn del
svn cp
svn mv

コマンドリファレンス: http://svnbook.red-bean.com/en/1.7/svn.ref.html

答え3

仕事だと思います。調和!私は何年もプレイしていませんが、あなたが要求するものとまったく一致すると思います...ホームページには次のように書かれています。

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

見て続けてニュースをお伝えください! ;)

答え4

あなたは絶対にできるディレクトリ構造の変更を実行し、rsyncあなたが知らないかもしれないいくつかの興味深いオプション、特に-Hハードリンクを保存するオプションがあります。

私自身のシナリオを説明します。これはあなたに効果があるかもしれませんが、少なくともあなたがそれを探してください。興味深い

想像する:

rsync他のディレクトリ(リモートシステムや外部ディスクなど)にコピーされるディレクトリツリーには、多くのファイルを含む大きなディレクトリがあります。

ディレクトリを再構成し、ファイルの名前を変更したり、ファイルを別のサブディレクトリに移動したいのですが、ファイル自体の内容は変更したくありません。

ただし、次回実行時に移動されたファイルrsync名または名前が変更されたすべてのファイルのデータが再コピーされます。そのファイルがすでにターゲットに存在しています(他の場所にあるにもかかわらず)。データを再コピーせずにファイルの場所を同期する方法は次のとおりです。クイック

明らかにテストシステムで最初に試してみました注意してくださいあなたのデータとして。

ソースデータがにあり、/tmp/src/dir/ターゲットレプリカがにあるとします/tmp/dst/。これはリモートrsyncターゲットでも機能します。

0.

設定例:

$ mkdir -p /tmp/src/dir; cd $_
$ fallocate -l 1000 a
$ fallocate -l 1000 b
$ fallocate -l 1000 c

$ tree /tmp/src
/tmp/src
`-- dir
    |-- a
    |-- b
    `-- c

1.

初期コピー:

$ mkdir /tmp/dst; rsync -havHP /tmp/src/dir /tmp/dst

この時点で、/tmp/src/dir/その中にあるすべてのファイルとディレクトリが/tmp/dst/

$ tree /tmp/dst
/tmp/dst
`-- dir
    |-- a
    |-- b
    `-- c

2.

ソース内のディレクトリ構造のハードリンクコピーを作成します。注:これは、ファイルやディレクトリが多い場合でも同様です。まもなく、メタデータのみコピーするためです。

$ cd /tmp/src/; mv dir dir.old
$ cp -rlp dir.old dir

# -l = link not copy
# -p = preserve permissions etc

三。

/tmp/src/dirファイルの移動や名前の変更など、から多くの変更を行います。この例では、すべてのファイルを新しいサブディレクトリに移動します。

$ cd /tmp/src/dir; mkdir sub
$ mv a b c sub
$ tree /tmp/src/dir
/tmp/src/dir
`-- sub
    |-- a
    |-- b
    `-- c

4.

古いディレクトリ構造と新しいディレクトリ構造を変更した後、rsyncターゲットの横にクイック、ターゲットのハードリンクのみコピーするため、ファイルの内容が変更されない限りデータはコピーされません。

$ cd /tmp/src
$ mv dir dir.new; mv dir.old dir
$ rsync -havHP --delete-after --no-inc-recursive \
/tmp/src/dir /tmp/src/dir.new /tmp/dst

rsyncの出力は、ファイルの内容を再送信しないことを示しています。

building file list ... 
9 files to consider
dir.new/
dir.new/sub/
dir.new/sub/a => dir/a
dir.new/sub/b => dir/b
dir.new/sub/c => dir/c

sent 165 bytes  received 45 bytes  420.00 bytes/sec
total size is 6.00K  speedup is 28.57

5.

最後に、ソースとターゲットの両方で、ソースコンテンツを新しいコンテンツに置き換えてクリーンアップdirします。dir.new

$ cd /tmp/src
$ rm -rf dir
$ mv dir.new dir

$ cd /tmp/dst
$ rm -rf dir
$ mv dir.new dir

上記の回避策はやや面倒で、ディレクトリ交換中に他の人がデータにアクセスすることに注意する必要がありますが、rsync場合によっては多くの時間を節約できる興味深い機能であることは明らかです。

関連情報