Filtro Rsync: copiando apenas um padrão

115

Estou tentando criar um diretório que hospede todos e somente meus PDFs compilados do LaTeX. Eu gosto de manter cada projeto em uma pasta separada, todos alojados em uma pasta grande chamada LaTeX . Então eu tentei correr:

rsync -avn *.pdf ~/LaTeX/ ~/Output/

que deve encontrar todos os pdfs em ~/LaTeX/ e transferi-los para a pasta de saída. Isso não funciona. Diz-me que não encontrou correspondências para " *.pdf ". Se eu deixar este filtro, o comando listará todos os arquivos em todas as pastas do projeto em LaTeX. Então, é um problema com o filtro * .pdf. Tentei substituir ~/ pelo caminho completo do meu diretório inicial, mas isso não teve efeito.

Estou usando o zsh. Eu tentei fazer a mesma coisa no bash e até com o filtro que listou todos os arquivos em todos os subdiretórios ... O que está acontecendo aqui?

Por que o rsync não entende meu filtro somente em pdf?

OK. Então atualize: Não, eu estou tentando

rsync -avn --include="*/" --include="*.pdf" LaTeX/ Output/

E isso me dá toda a lista de arquivos. Eu acho que porque tudo combina com o primeiro padrão ...

    
por Seamus 16.09.2010 / 17:09

8 respostas

220

O Rsync copia a (s) origem (s) para o destino. Se você passar *.pdf como origens, o shell expandirá isso para a lista de arquivos com a extensão .pdf no diretório atual. Nenhuma travessia recursiva acontece porque você não passou nenhum diretório como fonte.

Portanto, você precisa executar rsync -a ~/LaTeX/ ~/Output/ , mas com um filtro para informar ao rsync para copiar somente arquivos .pdf . As regras de filtragem do Rsync podem parecer assustadoras quando você lê o manual, mas você pode construir muitos exemplos com apenas algumas regras simples.

  • Inclusões e exclusões:

    • A exclusão de arquivos por nome ou por local é fácil: --exclude=*~ , --exclude=/some/relative/location (relativo ao argumento de origem, por exemplo, isso exclui ~/LaTeX/some/relative/location ).
    • Se você quiser corresponder apenas alguns arquivos ou locais, inclua-os, inclua todos os diretórios que levam a eles (por exemplo, com --include=*/ ) e exclua o restante com --exclude='*' . Isso porque:
    • Se você excluir um diretório, isso excluirá tudo abaixo dele. Os arquivos excluídos não serão considerados.
    • Se você incluir um diretório, isso não incluirá automaticamente seu conteúdo. Nas versões recentes, --include='directory/***' fará isso.
    • Para cada arquivo, a primeira regra de correspondência se aplica (e qualquer coisa nunca correspondida é incluída).
  • Padrões:

    • Se um padrão não contiver um / , ele será aplicado ao diretório sans do nome do arquivo.
    • Se um padrão terminar com / , ele será aplicado apenas aos diretórios.
    • Se um padrão começar com / , ele será aplicado a todo o caminho do diretório que foi passado como um argumento para rsync .
    • * de qualquer substring de um único componente de diretório (isto é, nunca corresponde a / ); ** corresponde a qualquer substring de caminho.
  • Se um argumento de origem terminar com / , seu conteúdo será copiado ( rsync -r a/ b cria b/foo para cada a/foo ). Caso contrário, o próprio diretório será copiado ( rsync -r a b cria b/a ).

Assim, aqui precisamos incluir *.pdf , incluir diretórios que os contenham e excluir todo o resto.

rsync -a --include='*.pdf' --include='*/' --exclude='*' ~/LaTeX/ ~/Output/

Observe que isso copia todos os diretórios, mesmo aqueles que não contêm nenhum arquivo ou subdiretório correspondente contendo um. Isso pode ser evitado com a opção --prune-empty-dirs (não é uma solução universal, já que você não pode copiar um diretório nem mesmo combinando-o explicitamente, mas esse é um requisito raro).

rsync -am --include='*.pdf' --include='*/' --exclude='*' ~/LaTeX/ ~/Output/
    
por 28.09.2010 / 21:23
20
rsync -av --include="*/" --include="*.pdf" --exclude="*" ~/Latex/ ~/Output/ --dry-run

O padrão é incluir tudo, então você deve explicitamente excluir tudo após incluindo os arquivos que deseja transferir. Remova o --dry-run para realmente transferir os arquivos.

Se você começar com:

--exclude '*' --include '*.pdf'

Em seguida, a correspondência desejada excluirá tudo imediatamente.

Se você tentar:

--include '*.pdf' --exclude '*' 

Em seguida, somente os arquivos pdf na pasta de nível superior serão transferidos. Ele não seguirá nenhum diretório, já que eles são excluídos por '*'.

    
por 28.09.2010 / 22:28
14

Se você usar um padrão como *.pdf , o shell "expandirá" esse padrão, ou seja, ele substituirá o padrão por todas as correspondências no diretório atual. O comando que você está executando (neste caso, rsync) não está ciente do fato de que você tentou usar um padrão.

Quando você está usando o zsh , existe uma solução fácil: O padrão ** pode ser usado para combinar pastas recursivamente. Tente isto:

rsync -avn ~/LaTeX/**/*.pdf ~/Output/
    
por 16.09.2010 / 19:25
12

Você pode usar find e uma lista intermediária de arquivos ( files_to_copy ) para resolver seu problema. Verifique se você está no seu diretório inicial e, em seguida:

find LaTeX/ -type f -a -iname "*.pdf" > files_to_copy && rsync -avn --files-from=files_to_copy ~/ ~/Output/ && rm files_to_copy

Testado com Bash.

    
por 27.09.2010 / 18:56
8

A julgar pela seção "INCLUDE / EXCLUDE PADRÃO REGRAS" da página de manual , a maneira de fazer isso é

rsync -avn --include="*/" --include="*.pdf" ~/Latex/ ~/Output/

A diferença crítica entre esta e a resposta do kbrd é o --include="*/" flag, que diz ao rsync para ir em frente e copiar todos os diretórios que encontrar, seja qual for o nome. Isso é necessário porque o rsync não recorre a um subdiretório, a menos que tenha sido instruído a copiar esse subdiretório.

Além disso, observe que as aspas impedem que o shell tente expandir os padrões para nomes de arquivos em relação ao diretório atual e executar um dos seguintes procedimentos:

  1. Sucedendo e bagunçando seu filtro (não muito provavelmente no meio de uma bandeira como essa, embora você nunca saiba quando alguém criará um arquivo chamado --include=foo.pdf ...)

  2. Falha e, potencialmente, produz um erro em vez de executar o comando (como você descobriu que o zsh faz por padrão).

por 16.09.2010 / 21:01
4

Que tal isso:

rsync -avn --include="*.pdf" ~/Latex/ ~/Output/
    
por 16.09.2010 / 18:00
3

Aqui está algo que deve funcionar sem usar o recurso "Localizar". A diferença das respostas já postadas é a ordem das regras de filtragem. Regras de filtro em um comando rsync funcionam muito como regras de iptable, a primeira regra que um arquivo corresponde é aquela que é usada. Na página de manual :

As the list of files/directories to transfer is built, rsync checks each name to be transferred against the list of include/exclude patterns in turn, and the first matching pattern is acted on: if it is an exclude pattern, then that file is skipped; if it is an include pattern then that filename is not skipped; if no matching pattern is found, then the filename is not skipped.

Assim, você precisa de um comando da seguinte forma:

rsync -avn --include="**.pdf" --exclude="*" ~/LaTeX/ ~/Output/

Observe o padrão "** .pdf". De acordo com a página de manual :

if the pattern contains a / (not counting a trailing /) or a "**", then it is matched against the full pathname, including any leading directories. If the pattern doesn’t contain a / or a "**", then it is matched only against the final component of the filename. (Remember that the algorithm is applied recursively so "full filename" can actually be any portion of a path from the starting directory on down

No meu pequeno teste, isso funciona recursivamente na árvore de diretórios e seleciona apenas os pdfs.

    
por 28.09.2010 / 17:13
2

Esta é a minha solução preferida:

find source_dir -iname '*.jpg' -print0 |  rsync -0 -v --files-from=- . destination_dir/

O comando find é mais fácil de entender do que as regras de inclusão / exclusão de rsync : -)

Se você deseja copiar apenas arquivos PDF, basta alterar .jpg para .pdf

    
por 11.03.2015 / 12:01