변경된 디렉토리 트리에서 파일을 "검색하고 바꾸는" Bash 명령

변경된 디렉토리 트리에서 파일을 "검색하고 바꾸는" Bash 명령

내 서버의 중앙 저장소에 연구용 파일 모음을 구축했습니다. 나는 지속적으로 저장소를 관리하고 디렉토리의 이름을 바꾸고 이동하여 디렉토리의 트리 구조를 변경합니다. 나는 저장소에 있는 파일의 로컬 복사본을 지속적으로 업데이트하고 편집하고 있습니다. 이로 인해 저장소에 클라이언트의 새 파일로 대체해야 하는 파일이 있지만 디렉터리 트리가 변경되는 상황이 발생합니다.

rsync아직 배우지 않은 옵션이 있나요 ?디렉터리 트리 변경 시 rsync가 작동하지 않는 것으로 나타났습니다. 나는 테스트했다rsync --existing

아니면 좀 있나요?명령줄 -fu사용 find하고 mv?웹 등을 검색해 보세요.자원찾기만 찾아서 바꾸기파일에예.

-- 업데이트 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 중 하나인 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용 파일 동기화 도구입니다. 이를 통해 파일 및 디렉터리 모음의 두 복사본을 다른 호스트(또는 동일한 호스트의 다른 디스크)에 저장하고 별도로 수정한 다음 각 복사본의 변경 사항을 다른 복사본에 전파하여 업데이트할 수 있습니다.

살펴보고 계속해서 소식을 전해 주세요! ;)

답변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어떤 경우에는 많은 시간을 절약할 수 있는 흥미로운 기능임은 분명합니다.

관련 정보