Filtro Rsync: copiando apenas um padrão

128

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

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

que deve encontrar todos os PDFs ~/LaTeX/e transferi-los para a pasta de saída. Isso não funciona. Diz-me que não foi encontrado nenhum resultado para " *.pdf". Se eu deixar de fora esse filtro, o comando listará todos os arquivos em todas as pastas do projeto no LaTeX. Portanto, há um problema com o filtro * .pdf. Tentei substituir ~/pelo caminho completo para o meu diretório pessoal, mas isso não teve efeito.

Estou usando o zsh. Tentei fazer o mesmo no bash e até com o filtro que listava todos os arquivos em todos os subdiretórios ... O que está acontecendo aqui?

Por que o rsync não está entendendo meu filtro apenas de pdf?


ESTÁ BEM. Então atualize: Não, estou tentando

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

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

Seamus
fonte
Você parece estar certo ... Acho que minha resposta (usando o **padrão zsh ) deve funcionar.
Marcel Stimberg

Respostas:

248

TL, DR:

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

Rsync copia a (s) fonte (s) para o destino. Se você passar *.pdfcomo fontes, o shell o expande para a lista de arquivos com a .pdfextensão no diretório atual. Nenhuma passagem recursiva acontece porque você não passou nenhum diretório como fonte.

Então você precisa executar rsync -a ~/LaTeX/ ~/Output/, mas com um filtro para dizer ao rsync para copiar .pdfapenas os arquivos. As regras de filtro do Rsync podem parecer assustadoras quando você lê o manual, mas você pode criar muitos exemplos com apenas algumas regras simples.

  • Inclusões e exclusões:

    • Excluindo arquivos por nome ou localização é fácil: --exclude=*~, --exclude=/some/relative/location(em relação ao argumento origem, por exemplo, este exclui ~/LaTeX/some/relative/location).
    • Se você deseja apenas corresponder a alguns arquivos ou locais, inclua-os, inclua todos os diretórios que os levam (por exemplo, com --include=*/) e exclua o restante com --exclude='*'. Isto é 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 correspondente se aplica (e qualquer coisa que nunca corresponda é incluída).
  • Padrões:

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


Portanto, 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 arquivos ou subdiretórios correspondentes. Isso pode ser evitado com a --prune-empty-dirsopção (não é uma solução universal, pois você não pode copiar um diretório nem mesmo combiná-lo explicitamente, mas esse é um requisito raro).

rsync -am --include='*.pdf' --include='*/' --exclude='*' ~/LaTeX/ ~/Output/
Gilles
fonte
Ao contrário da minha solução (usando o **padrão do zsh ), isso recria a estrutura de diretórios no diretório de destino. Eu não tenho certeza se é isso que o OP quer ...
Marcel Stimberg
Quero incluir apenas um diretório e excluir o restante de todo o diretório no /etc/lsyncd/lsyncd.conf.luaarquivo. Tem alguma ideia?
Dhaduk Mitesh
@DhadukMitesh Não estou familiarizado com lsyncd. Você deve fazer isso como uma nova pergunta.
Gilles
25
rsync -av --include="*/" --include="*.pdf" --exclude="*" ~/Latex/ ~/Output/ --dry-run

O padrão é incluir tudo, portanto, você deve excluir explicitamente tudo depois de incluir os arquivos que deseja transferir. Remova o --dry-run para realmente transferir os arquivos.

Se você começar com:

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

A correspondência gananciosa excluirá tudo imediatamente.

Se você tentar:

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

Somente os arquivos pdf na pasta de nível superior serão transferidos. Ele não seguirá nenhum diretório, pois esses são excluídos por '*'.

jmanning2k
fonte
2
A partir de 17/03/2014, essa é a melhor resposta, pois resolve exatamente a questão dos pôsteres originais . Por favor vote! Se você adicionar --prune-empty-dirs(ou atalho -m), você ainda poupa muitos diretórios vazios no destino, exceto, é claro, que deseja que eles sejam um lembrete ou um plano estrutural.
porg
1
Melhor resposta, --include = "* /" é a chave.
Martin Konicek
Quero incluir apenas um diretório e excluir o restante de todo o diretório no /etc/lsyncd/lsyncd.conf.luaarquivo. Tem alguma ideia?
Dhaduk Mitesh
15

Se você usar um padrão como *.pdf, o shell "expande" esse padrão, ou seja, substitui o padrão por todas as correspondências no diretório atual. O comando que você está executando (neste caso, rsync) não tem conhecimento 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 corresponder pastas recursivamente. Tente o seguinte:

rsync -avn ~/LaTeX/**/*.pdf ~/Output/
Marcel Stimberg
fonte
Isso não copiaria todos os pdfs de algum lugar do diretório atual e tudo, de ~ / LaTeX / a ~ / Output?
SamB 16/09
Eu acho que você quis dizer rsync -avn ~/LaTeX/**/*.pdf ~/Output, mas a solução com --includeé mais escalável de qualquer maneira.
Adam Byrtek
Desculpe, corrigi o comando que eu digitei incorretamente ... Concordo que o comando de inclusão (na versão do SamB) é melhor, embora seja um pouco mais complicado e específico para o rsync, **podendo ser útil em outras situações também.
Marcel Stimberg
1
O Bash 4 adotou o mesmo recurso. Ah, e você não precisa de rsync aqui, o cp fará. Em alguns sistemas, se houver muitos arquivos, isso ajuda cd ~/Latex && cp -p **/*.pdf ~/Outputa evitar um erro de "linha de comando muito longa".
Gilles
1
Observe que os padrões do rsync usados ​​nos filtros de inclusão e exclusão também possuem um ** que faz a mesma coisa. Você pode escapar * de outras conchas colocando-as entre aspas.
Dan Pritts
13

Você pode usar finde uma lista intermediária de arquivos ( files_to_copy) para resolver seu problema. Verifique se você está no 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.

Derek Frye
fonte
Eu acho que encontrar é a solução mais robusta, mas eu optaria por usar a -execopção find ou usando xargs. Algo como:find LaTeX/ -type f -iname "*.pdf" -print0 | xargs -0 -i rsync -avn {} Output/
Steven D
Sim ... eu sugiro que encontre também ... embora eu imagine que o rsync deve ser capaz de fazer isso.
Gabe.
Essa também é uma solução interessante para um problema mais difícil: presumivelmente eu poderia usá-la para excluir arquivos cuja classe de documento é standaloneou que não possui um .texarquivo com o mesmo nome, pois essas serão imagens incluídas em algum documento ...
Seamus
2
A opção rsync --files-fromaceita a leitura de stdin. Isso funcionaria # find LaTeX/ -type f -a -iname "*.pdf" | rsync -avn --files-from=- ~/ ~/Output/
Juan Calero
9

A julgar pela seção "INCLUIR / EXCLUIR REGRAS DE PADRÃO" 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 é a --include="*/"flag, que diz ao rsync para seguir em frente e copiar os diretórios que encontrar, independentemente do nome. Isso é necessário porque o rsync não recursará em 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 siga um destes procedimentos:

  1. Como obter êxito e atrapalhar o seu filtro (não é muito provável 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 produzindo um erro em vez de executar o comando (como você descobriu o zsh por padrão).

SamB
fonte
Portanto, isso copiará apenas os PDFs e a estrutura de diretórios, enquanto o kbrd copiará os arquivos, mas ignorará a estrutura?
Seamus
1
Hmm. Na verdade, isso ainda parece tentar copiar tudo, eu acho, porque é isso que ele faz sem o filtro, de modo que includecoisas extras já existentes lá não mudam nada. Se você ver o que quero dizer ...
Seamus
7
Você precisa --exclude="*"depois do --include="*.pdf", ou isso irá transferir tudo.
precisa saber é o seguinte
@ jmanning2k: Ah. Bom saber!
SamB
4

Que tal agora:

rsync -avn --include="*.pdf" ~/Latex/ ~/Output/
kbyrd
fonte
Não, man rsynccoloca o filtro após as opções e antes da origem / destino. Eu tentei isso e não funcionou
Seamus
Você encontra arquivos .pdf na pasta atual, mas não recursivamente, como eu quero. (a aopção é para arquivar e, entre outras coisas, torna a cópia recursiva.
Seamus
1
Opa, meu mal. Eu atualizei minha resposta.
Kbyrd 16/09/10
+1 por estar tão perto e por me dar uma pista sobre como encontrar o material relevante na página de manual. (Espero que eu tenha acertado. :-)
SamB 16/09
3

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

À medida que a lista de arquivos / diretórios a serem transferidos é criada, o rsync verifica cada nome a ser transferido à lista de padrões de inclusão / exclusão, por sua vez, e o primeiro padrão correspondente é acionado: se for um padrão de exclusão, esse arquivo será pulado; se for um padrão de inclusão, esse nome de arquivo não será ignorado; se nenhum padrão correspondente for encontrado, o nome do arquivo não será ignorado.

Portanto, você precisa de um comando da seguinte maneira:

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

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

se o padrão contiver um / (sem contar um / final) ou um "**", será comparado com o nome do caminho completo, incluindo os diretórios principais. Se o padrão não contiver um / ou um "**", será comparado apenas com o componente final do nome do arquivo. (Lembre-se de que o algoritmo é aplicado recursivamente para que "nome completo do arquivo" possa realmente ser qualquer parte de um caminho do diretório inicial para baixo

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

Steven D
fonte
Como exatamente você testou? De acordo com meu entendimento da documentação e minha verificação experimental, seu comando deve copiar apenas *.pdfno diretório de nível superior (mas não ~/LaTeX/foo/bar.pdf).
Gilles
@Gilles Crud. Você está certo. Eu jurei que testei isso e funcionou, mas não consigo recriá-lo. E agora que eu realmente li a página de manual que citei, faz sentido que não funcione. Resmungar.
Steven D
1
Bem, eu descobri onde meu teste estava errado. Meu "pequeno teste" estava em um diretório que possuísse arquivos .tex e .pdf. Criei um subdiretório "test" e um test.pdf e test.tex nesse subdiretório. No entanto, não percebi que havia um test.pdf no meu diretório de nível superior, provavelmente devido a uma rápida experiência do LaTeX que fiz.
Steven D
Eu ainda não entendo o **. Seria bom ter um exemplo disso. ;)
buhtz
2

Esta é a minha solução preferida:

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

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

Se você deseja copiar apenas arquivos pdf, altere .jpgpara.pdf

guettli
fonte