pastas de mesclagem linux: rsync?

13

Tenho duas cópias de uma pasta

src/
dest/

Quero mesclá-los, fazendo o seguinte:

Se um arquivo estiver apenas dentro src, quero que ele seja movido paradest

Se um arquivo estiver apenas dentro dest, quero que seja ignorado o IE deixado em paz.

Se um arquivo estiver nos dois e tiver conteúdo idêntico (IE, mesmo tamanho e data), exclua desrc

Se um arquivo estiver nos dois e não tiver conteúdo idêntico, deixe para trás srcpara que eu possa mesclá-los manualmente.

Apenas um número muito pequeno de arquivos (entre 0% e 5% do total de arquivos) deve estar nesta última categoria, mas não sei como separar o em ambos e o mesmo de ambos, mas diferente.

Eu tentei descobrir como fazer isso, rsyncmas sem sucesso até agora.

David Oneill
fonte

Respostas:

17

Eu realizei apenas testes de funcionalidade limitados, portanto, tenha cuidado com este comando (--dry-run):

rsync -avPr --ignore-existing --remove-source-files src/ dest

Observe o trailing / como isso recorrerá ao src em vez de copiar o próprio src, isso deve manter os caminhos existentes.

Ao usar o sinalizador --ignore-existente em combinação com o sinalizador --remove-source-files, você excluirá apenas os arquivos do src sincronizados do src para o destino, ou seja, os arquivos que não existiam anteriormente apenas no destino.

Para excluir arquivos não sincronizados, ou seja, aqueles que já existiam em dest / como em src /, você pode usar:

for file in `find src/ -type f`; do diff $file `echo $file | sed 's/src/dest/'` && rm $file || echo $file; done

ou

find src -type f -exec bash -c 'cmp -s "$0" "${0/#src/dest}" && rm "$0"' {} \;

se os nomes de arquivos puderem conter espaços em branco / novas linhas / ... Em relação ao comentário de Gilles sobre caracteres especiais, isso certamente é algo a ser lembrado e existem muitas soluções, a mais simples seria passar um -i para rm, o que solicitará antes de toda exclusão. Desde que src /, ou seu caminho pai, seja fornecido para localizar, no entanto, o caminho completo deve resultar em todos os nomes de arquivos sendo tratados adequadamente pelos comandos diff e rm sem citar.

Tok
fonte
correção: esse comando não removerá os arquivos do src se uma cópia idêntica já existir no dest
Tok
Sim :(. Essa é a parte que eu estou achando difícil de descobrir. #
David Oneill
2
Bem, a boa notícia é que você pode resolvê-lo de forma independente sem muito incômodo: for file in `find src/ -type f`; do diff $file `echo $file | sed 's/src/dest/'` && rm $file || echo $file; done(você pode pular o || echo $filese quiser, ele é incluído para ser completo)
Tok
Nifty: era disso que eu precisava. Edite isso na sua resposta, e eu aceito!
David Oneill em
@Tok: Seu comando irá engasgar com nomes de arquivos que contenham caracteres especiais (espaço em branco \?*[, inicial -). Você precisa usar aspas duplas em torno das substituições de variáveis , passar --para os utilitários antes dos nomes dos arquivos, usar em find … -exec …vez de analisar a saída de find. Com um rmcomando na mistura, esta é uma receita para o desastre.
Gilles 'SO- stop be evil'
6

uníssono é a ferramenta que você está procurando. Tente unison-gtk se você preferir um gui. Mas não acho que ele excluirá arquivos semelhantes: o unison tenta ter os dois diretórios idênticos. No entanto, ele irá facilmente 1) identificar quais arquivos devem ser copiados; 2) quais precisam de mesclagem manual.

simonp
fonte
Ele não faz exatamente o que o OP pede, mas parece que ele alcança o objetivo final do OP. 1
Ryan C. Thompson
+1 Infelizmente, o servidor em que estou executando isso não possui uníssono instalado, nem tenho permissões para instalá-lo. Mas isso pode ser uma boa resposta para outra pessoa.
David Oneill
1
Você pode fazer o download do executável uníssono em seas.upenn.edu/~bcpierce/unison//download/… . Instale-o em algum lugar do seu diretório pessoal, é apenas um arquivo.
JooMing
2

O script a seguir deve fazer as coisas razoavelmente. Ele move os arquivos da origem para o destino, nunca substituindo um arquivo e criando diretórios conforme necessário. Os arquivos de origem que possuem um arquivo diferente correspondente no destino são deixados em paz, assim como os arquivos que não são arquivos ou diretórios regulares (por exemplo, links simbólicos). Os arquivos restantes na fonte são aqueles para os quais há um conflito. Cuidado, eu não testei nada.

cd src
find . -exec sh -c '
    set -- "/path/to/dest/$0"
    if [ -d "$0" ]; then #  the source is a directory 
      if ! [ -e "$1" ]; then
        mv -- "$0" "$1"  # move whole directory in one go
      fi
    elif ! [ -e "$0" ]; then  # the source doesn't exist after all
      :  # might happen if a whole directory was moved
    elif ! [ -e "$1" ]; then  # the destination doesn't exist
      mv -- "$0" "$1"
    elif [ -f "$1" ] && cmp -s -- "$0" "$1"; then  # identical files
      rm -- "$0"
    fi
  ' {} \;

Outra abordagem seria fazer uma união montar um diretório acima do outro, por exemplo, com funionfs ou unionfs-fuse .

Gilles 'SO- parar de ser mau'
fonte