Comando Bash ajuda a “pesquisar e substituir” o arquivo na mudança da árvore de diretórios

1

O cenário:

Eu construí uma coleção de arquivos para pesquisa em um repositório central no meu servidor. Eu estou curando continuamente o repositório alterando a estrutura de árvore do diretório renomeando e movendo diretórios. E estou continuamente atualizando e editando cópias locais de arquivos no repositório. Isso cria a situação em que tenho um arquivo no repositório que precisa ser substituído por um arquivo mais novo no cliente, mas a árvore de diretórios foi alterada desde então.

Existe uma opção rsync que ainda não aprendi? Não encontro o rsync trabalhando com a mudança de árvore de diretórios. Testei rsync --existing

Ou existe alguma linha de comando usando find e mv ? Pesquisando na rede e outros resources encontra apenas a pesquisa e substitui no arquivo exemplos.

ATUALIZAÇÃO 1 Se eu pudesse, daria pontos para @meuh e @Lqueryvg para respostas fantásticas. Eu vejo @ css1971 adicionado à sua resposta e fiz um strong argumento para o gerenciamento de arquivos de subversão. Infelizmente eu fiquei sem tempo e o sistema de prêmios simplesmente escolheu sua resposta favorita para mim. Eu achei toda a resposta incrivelmente informativa. Eu quero agradecer a todos vocês. Estou ansioso para algum tempo próximo próximo fim de semana para dar-lhes todos os comentários mais pensativos.

    
por xtian 05.03.2016 / 16:52

4 respostas

2

[expandido do meu comentário para o OP]. Crie no repositório um diretório flat que contém uma cópia de todos os arquivos, mas em uma lista simples. As cópias seriam hard links, por isso não ocupe espaço. Faça o mesmo na máquina local. Você pode, então, rsync do diretório flat local para o diretório flat remoto. Isso atualizará todos os arquivos remotos, já que o rsync preservará links físicos remotos se você usar --inplace . Os diretórios planos podem ser criados pelo script antes do rsync e removidos depois.

Aqui está uma prova de script de teste de conceito:

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

Para a direção inversa, após o dosync ter transferido seus modifs locais para o repositório, você pode apenas rm -fr local e "rsync -a repo / local" para copiar o repositório completo para o sistema de arquivos local. Em vez disso, você pode reduzir o número de arquivos que precisam ser transferidos para apenas os novos no repositório usando uma técnica semelhante:

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
}

Isso move as árvores planas para o repositório e dirs locais, respectivamente, para que o rsync possa ver os arquivos com link físico e evitar copiá-los. (Obviamente, os diretórios planos devem ter o mesmo nome desta vez).

Se você tem apenas um único arquivo conhecido que você alterou, você pode usar find no repositório para obter sua nova posição na árvore e rsync o arquivo para lá. Por exemplo:

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

Isso pressupõe que o repositório esteja montado, caso contrário, você pode usar ssh repo find... . Se você não pode usar o ssh no repositório, você pode usar o rsync para um destino local fictício para obter a lista de arquivos e extrair aquele que você deseja:

to=$(rsync -a --list-only repo dummy | awk '/\/'"$file"'$/{print $NF}')
    
por 09.04.2016 / 09:19
2

Acho que o rsync é a ferramenta errada, assim como find e mv. Minha recomendação é, em vez disso, fazer uso de um sistema de gerenciamento de configuração de software. Estes incluem Subversion, Git, Mercurial, Bazaar entre outros. Todos os quais podem lidar facilmente com alterações de estrutura de árvore.

Na estrutura que você descreve, você tem a estrutura de árvore A em seu sistema cliente e uma estrutura de árvore de repositório B em um local secundário, possivelmente local, possivelmente remoto.

Quando você tem atualizações para ambos, agora você tem alterações concorrentes que devem ser aplicadas na ordem correta para que seus dois repositórios permaneçam consistentes. Se eles são aplicados fora de ordem, você entra na situação em que está agora, onde as mudanças em uma não podem ser aplicadas diretamente na outra, porque as estruturas não existem mais.

No momento, não há opções para o rsync, o que permite saber automaticamente quais alterações devem ser aplicadas para trazer ambos os repositórios à consistência. Não é isso que ele foi projetado para fazer. Certamente, um repositório pode parecer exatamente igual ao outro, mas requer que apenas um lado seja alterado por vez. Exemplo alternando as alterações para A, depois para B, depois para A. A qualquer momento, você precisa designar uma das estruturas A ou B como mestre e sincronizar as alterações em apenas uma direção de cada vez. / p>

Eu também não acredito que exista um comando commandline-fu simples que irá alcançar o resultado que você está procurando, então agora você está no reino da programação shell.

Onde você tem alterações apenas na estrutura de árvore de B e apenas no conteúdo do arquivo de A, é uma tarefa relativamente simples procurar pelos nomes de arquivo do arquivos alterados e adquirir os novos caminhos desses arquivos em B, em seguida, modifique a estrutura da árvore de A para corresponder. Este somente funciona se os nomes dos arquivos forem exclusivos.

O pseudocódigo que traria a estrutura de A para consistência com B seria parecido com:

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

Uma vez que as estruturas em árvore estão em sincronia, A pode ser copiado diretamente para exatamente os mesmos caminhos em B. A opção --update do rsync pode ser usada para sobrescrever arquivos antigos por novos em ambas as direções.

Alguns exemplos de código de shell para copiar arquivos alterados localmente para o repositório existente, usando find como o seletor de nome de arquivo.

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

Para importar um sistema de arquivos para o subversion, por exemplo, como um dos mais simples de usar SCMs:

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

Em seguida, verifique o repo e faça alterações locais.

$ 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:

Confirme as alterações no repo.

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

Committed revision 2.

A partir deste ponto, use a ferramenta svn como parte de seu fluxo de trabalho normal para manipular o repositório.

Comandos úteis:

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

Referência de comando: link

    
por 06.04.2016 / 11:09
0

Acho que é um trabalho para Unison ! Eu não brinquei com isso há anos, mas acho que poderia fazer exatamente o que você está pedindo ... A home page diz:

Unison is a file-synchronization tool for OSX, Unix, and Windows. It allows two replicas of a collection of files and directories to be stored on different hosts (or different disks on the same host), modified separately, and then brought up to date by propagating the changes in each replica to the other.

Dê uma olhada e mantenha-nos informados! ;)

    
por 09.04.2016 / 01:02
0

Você definitivamente pode usar rsync com uma estrutura de diretório em constante mudança e há algumas opções interessantes que você pode não estar ciente, em particular -H que preserva os hard links.

Vou descrever meu próprio cenário. Isso pode ou não funcionar para você, mas espero que pelo menos você ache interessante.

CENÁRIO:

Você tem um diretório grande com muitos arquivos em uma árvore de diretórios copiado via rsync para outro diretório (possivelmente em uma máquina remota ou disco externo).

Você deseja reestruturar seu diretório, talvez renomeando arquivos e / ou movendo -los para diferentes subdiretórios, mas sem alterar o conteúdo do arquivos em si.

Mas, da próxima vez que você executar rsync , ele copia novamente os dados de todos os arquivos que foram movidos ou cujos nomes foram alterados, mesmo que eles já existam (embora em locais diferentes) no destino. O seguinte é uma maneira de sincronizar os locais dos arquivos sem copiar os dados novamente e é rápido .

Obviamente, tente isso primeiro em um sistema de teste e cuidado com seus dados.

Digamos que seus dados de origem estejam em /tmp/src/dir/ e a cópia de destino esteja em /tmp/dst/ . Observe que isso também funciona para destinos rsync remotos.

0.

Exemplo de configuração:

$ 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.

Cópia inicial:

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

Neste ponto, /tmp/src/dir/ e todos os arquivos e diretórios foram copiados para /tmp/dst/ .

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

2.

Faça cópias de link físico de sua estrutura de diretórios na origem. Nota: mesmo com muitos arquivos e diretórios, isso é muito rápido , porque é apenas copiar metadados:

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

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

3.

Faça muitas alterações em /tmp/src/dir , incluindo mover e renomear arquivos. Neste exemplo, moverei todos os arquivos para um novo subdiretório.

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

4.

Depois de trocar as estruturas de diretório antiga e nova, o próximo rsync para o destino é rápido , porque ele só replica links físicos no destino e não copia dados, a menos que o conteúdo mudou.

$ 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

Observe que a saída do rsync mostra que ele não transfere o conteúdo do arquivo novamente:

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.

Por fim, arrume-se substituindo o original dir pelo novo dir.new na origem e no destino:

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

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

A solução acima é um pouco complicada e você deve ser cuidadoso com os outros que acessam os dados durante as trocas de diretório, mas é certamente uma característica interessante do rsync que pode economizar muito tempo em determinados cenários.

    
por 09.04.2016 / 01:41