Como posso fazer uma operação de “copiar se for alterada”?

30

Eu gostaria de copiar um conjunto de arquivos do diretório A para o diretório B, com a ressalva de que se um arquivo no diretório A é idêntico a um arquivo no diretório B, esse arquivo não deve ser copiado (e, portanto, seu tempo de modificação não deve ser atualizado). Existe uma maneira de fazer isso com ferramentas existentes, sem escrever meu próprio script para fazer isso?

Para elaborar um pouco sobre o meu caso de uso: estou autogerando um monte de arquivos .c em um diretório temporário (por um método que tem que gerar todos eles incondicionalmente), e quando eu os re-gerar, eu Gostaria de copiar apenas os que foram alterados no diretório de origem, deixando os inalterados intactos (com seus tempos de criação antigos) para que make saiba que não precisa recompilá-los. (Nem todos os arquivos gerados são .c arquivos, então eu preciso fazer comparações binárias ao invés de comparações de texto.)

(Como nota: isso cresceu fora da questão que eu perguntei em link , onde eu estava tentando acelerar o arquivo de script que eu estava usando para fazer esta operação, mas me ocorre que eu realmente deveria perguntar se há uma maneira melhor de fazer isso do que escrever meu próprio script - especialmente porque qualquer maneira simples de fazer isso em um shell script invocará algo como cmp em cada par de arquivos, e iniciar todos esses processos leva muito tempo.)

    
por Brooks Moses 24.01.2012 / 06:15

6 respostas

27

O rsync é provavelmente a melhor ferramenta para isso. Existem muitas opções neste comando, por isso leia a página de manual . Eu acho que você quer a opção --checksum ou --ignore-times

    
por 24.01.2012 / 06:47
7

Você pode usar a opção -u como cp da seguinte forma:

$ cp -u [source] [destination]

Da página do manual:

   -u, --update
       copy only when the SOURCE file is newer than the destination file or 
       when the destination file is missing
    
por 27.06.2014 / 19:26
6

Enquanto usar rsync --checksum é uma boa maneira geral de "copiar se alterado", no seu caso particular, existe uma solução ainda melhor!

Se você deseja evitar a recompilação desnecessária de arquivos, use o ccache que foi criado exatamente para essa finalidade! Na verdade, não só evitará recompilações desnecessárias de seus arquivos gerados automaticamente, como também acelerará as coisas sempre que você make clean e recompilará a partir do zero.

Em seguida, tenho certeza de que você perguntará: "É seguro?" Bem, sim, como o site aponta:

Is it safe?

Yes. The most important aspect of a compiler cache is to always produce exactly the same output that the real compiler would produce. This includes providing exactly the same object files and exactly the same compiler warnings that would be produced if you use the real compiler. The only way you should be able to tell that you are using ccache is the speed.

E é fácil de usar apenas adicionando-o como um prefixo na linha CC= do seu makefile (ou você pode usar links simbólicos, mas o caminho do makefile provavelmente é melhor).

    
por 31.01.2012 / 01:01
2

Isso deve fazer o que você precisa

diff -qr ./x ./y | awk '{print $2}' | xargs -n1 -J% cp % ./y/

Onde:

  • x é sua pasta atualizada / nova
  • y é o destino que você deseja copiar para
  • o awk vai pegar o segundo argumento da cada linha do comando diff (talvez você precise de algum material extra para nomes de arquivos com espaço - não pode tentar agora)
  • xargs -J% irá inserir o nome do arquivo para cp no local correto
por 25.01.2012 / 10:29
2

Eu gosto de usar unison em favor de rsync porque ele suporta múltiplos mestres, tendo já configurado minhas chaves ssh e vpn separadamente.

Então, no meu crontab de apenas um host, eu os deixo sincronizar a cada 15 minutos:

*/15 * * * * [ -z "$(pidof unison)" ] && (timeout 25m unison -sortbysize -ui text -batch -times /home/master ssh://192.168.1.12//home/master -path dev -logfile /tmp/sync.master.dev.log) &> /tmp/sync.master.dev.log

Então eu posso estar desenvolvendo em ambos os lados e as mudanças se propagam. De fato, para projetos importantes, eu tenho até 4 servidores espelhando a mesma árvore (3 rodam em uníssono do cron, apontando para o que não funciona). Na verdade, os hosts Linux e Cygwin são mistos - exceto por não esperar que os links suaves surjam no win32 fora do ambiente cygwin.

Se você seguir esse caminho, crie o espelho inicial no lado vazio sem o -batch , ou seja,

unison -ui text  -times /home/master ssh://192.168.1.12//home/master -path dev

Claro que há uma configuração para ignorar arquivos de backup, arquivos, etc .:

 ~/.unison/default.prf :
# Unison preferences file
ignore = Name {,.}*{.sh~}
ignore = Name {,.}*{.rb~}
ignore = Name {,.}*{.bak}
ignore = Name {,.}*{.tmp}
ignore = Name {,.}*{.txt~}
ignore = Name {,.}*{.pl~}
ignore = Name {.unison.}*
ignore = Name {,.}*{.zip}

    # Use this command for displaying diffs
    diff = diff -y -W 79 --suppress-common-lines

    ignore = Name *~
    ignore = Name .*~
    ignore = Path */pilot/backup/Archive_*
    ignore = Name *.o
    
por 02.02.2012 / 16:11
0

Embora rsync --checksum seja a resposta correta, observe que essa opção é incompatível com --times e que --archive inclui --times , portanto, se você quiser rsync -a --checksum , realmente precisará rsync -a --no-times --checksum .

    
por 21.03.2015 / 00:45