rsync usando regex para incluir apenas alguns arquivos

11

Estou tentando executar o rsync para copiar alguns arquivos recursivamente por um caminho com base em seu padrão de nome de arquivo, sem distinção entre maiúsculas e minúsculas . Isto é o que eu fiz para executar o rsync:

$ rsync -avvz --include ='*/' --include='.*[Nn][Aa][Mm][E].*' --exclude='*' ./a/ ./b/

Nada é copiado, a saída de depuração mostra:

[sender] hiding file 1Name.txt because of pattern *
[sender] hiding file 1.txt because of pattern *
[sender] hiding file 2.txt because of pattern *
[sender] hiding file Name1.txt because of pattern *
[sender] hiding directory test1 because of pattern *
[sender] hiding file NaMe.txt because of pattern *

Eu tentei usar: --include='*[Nn][Aa][Mm][E]*'e outras combinações, mas ainda não vai.

Alguma idéia de como usar o regex para incluir alguns arquivos?

user1957413
fonte
4
Por que você está usando o --exclude='*'?
2
portanto, exclui tudo o que não faz parte da inclusão.
'ocultando o arquivo 1Name.txt por causa do padrão ' isso indica: - "essa regra --exclude precisa estar no comando?" ou Se você deseja excluir alguns arquivos, por que um " ".
Akshay Patil

Respostas:

5

O rsync não fala regex. Você pode recrutar find e grep, apesar de ficar um pouco misterioso. Para encontrar os arquivos de destino:

find a/ |
grep -i 'name'

Mas todos são prefixados com "a /" - o que faz sentido, mas o que queremos terminar é uma lista de padrões de inclusão aceitáveis ​​para rsync e como o prefixo "a /" não funciona para rsync I ' vou removê-lo com corte:

find . |
grep -i 'name' |
cut -d / -f 2-

Ainda existe um problema - ainda vamos perder os arquivos nos subdiretórios, porque o rsync não pesquisa diretórios na lista de exclusões. Vou usar o awk para adicionar os subdiretórios de qualquer arquivo correspondente à lista de padrões de inclusão:

find a/ |
grep -i 'name' |
cut -d / -f 2- |
awk -F/ '{print; while(/\//) {sub("/[^/]*$", ""); print}}'

Tudo o que resta é enviar a lista para o rsync - podemos usar o argumento --include-from = - para fornecer uma lista de padrões para o rsync na entrada padrão. Então, no total:

find a/ |
grep -i 'name' |
cut -d / -f 2- |
awk -F/ '{print; while(/\//) {sub("/[^/]*$", ""); print}}' |
rsync -avvz --include-from=- --exclude='*' ./a/ ./b/

Observe que o diretório de origem 'a' é referido por dois caminhos diferentes - "a /" e "./a/". Isso é sutil, mas importante. Para tornar as coisas mais consistentes, farei uma alteração final e sempre me refiro ao diretório de origem como "./a/". No entanto, isso significa que o comando de corte precisa ser alterado, pois haverá um "./" extra na frente dos resultados de find:

find ./a/ |
grep -i 'name' |
cut -d / -f 3- |
awk -F/ '{print; while(/\//) {sub("/[^/]*$", ""); print}}' |
rsync -avvz --include-from=- --exclude='*' ./a/ ./b/
semana
fonte
Tentei executá-lo, tive problemas com o comando cut. Parece que -té um switch válido.
edit: eu quis dizer -t não é uma opção válida
desculpe, deve ser -d. Comecei usando sed e depois mudou para corte, porque eu pensei que era mais clara, mas esqueceu-se de editar os meus mandamentos: S
Acompanhamento: Tentei editar o script para obter argumentos ($ 1 = path_to_search, $ 2 como padrão para o egrep), pois estou correspondendo o nome do arquivo + a mistura de extensões. Que partes funciona bem, eu tenho a lista esperada, no entanto rsync falha ao copiar. Parece funcionar apenas com o diretório de caracteres de nome único, como no exemplo (a), meu palpite é que o comando cut deve ser modificado para cortar caracteres com base no diretório pai / ou fonte? Meio perdido de como fazer isso:
user1957413
Ah sim, você está certo. Ele deve funcionar em um nome de diretório de qualquer tamanho, mas falhará assim que você se referir a um diretório fora do diretório atual (porque haverá um número diferente de barras na parte do prefixo). Para corrigir isso, provavelmente é mais fácil usar sed em vez de cortar, como: sed "s#^$1/*##" buuuut que quebrará em caminhos que contenham um #. Para consertar isso, precisamos citar o nome do diretório recebido: prefix=$(echo "$1" | sed 's#/#\\/#g')e então sed "s/^$prefix\\/*//" os sutilezas da citação do bash são um pouco de pesadelo;)
sqweek
7

Eu sugeriria usar a opção de filtro do rsync. Para o seu exemplo, digite:

rsync -vam -f'+ *[Nn][Aa][Mm][E]*' -f'+ */' -f'- *' a b

a primeira regra de filtro informa ao rsync quais padrões incluir. A segunda regra é necessária para solicitar ao rsync que inspecione todos os diretórios em sua travessia. Para impedir a inclusão de diretórios vazios, eles são excluídos explicitamente por -mopção. A última regra de filtro diz ao rsync para descartar todos os padrões restantes que ainda não correspondiam até o momento.

sparkie
fonte
Doce. Isso funcionou também. Eu estava recebendo a pasta a dentro de b, que foi corrigida usando a / b / como origem e destino. Obrigado!
precisa saber é o seguinte
Use -f '+ * [Nn] [Aa] [Mm] [E] **' (duas estrelas no final) para incluir o conteúdo de todos os diretórios com um nome específico.
Phobic
2

Se você usar o ZSH, poderá usar o sinalizador (#i) para desativar a distinção entre maiúsculas e minúsculas. Exemplo:

$ touch NAME
$ ls (#i)*name*
NAME

O ZSH também suporta exclusões, que são especificadas como o caminho regular, mas têm uma inicial ~

$ touch aa ab ac
$ ls *~*c
aa ab

Você pode encadear exclusões:

$ ls *~*c~*b
aa

Finalmente, você pode especificar que tipo de arquivo você deseja retornar (diretório, arquivo, etc). Isso é feito com (/) para o diretório e (.) Para o arquivo.

$ touch file
$ mkdir dir
$ ls *(.)
file

Com base em tudo isso, eu faria esse comando como:

rsync -avvz *(/) (#i)*name* ./a/ ./b/

(Não vejo necessidade de exclusão com esses seletores)

Matthew Franglen
fonte
1

A resposta do @ sqweek acima é impressionante, embora eu suspeite que ele tenha um bug em seu awkscript para gerar diretórios principais, pois isso me dá, por exemplo:

$ echo a/b/c/d | awk -F/ '{print; while(/\//) {sub("/[^/]*", ""); print}}'
a/b/c/d
a/c/d
a/d
a

Consegui corrigi-lo usando gensub:

$ echo a/b/c/d | awk -F/ '{print; while(/\//) { $0=gensub("(.*)/[^/]*", "\\1", "g"); print}}'
a/b/c/d
a/b/c
a/b
a

Portanto, sua solução completa, com o awkbit alterado, seria:

find ./a/ |
grep -i 'name' |
cut -d / -f 3- |
awk -F/ '{print; while(/\//) { $0=gensub("(.*)/[^/]*", "\\1", "g"); print}}' |
rsync -avvz --include-from=- --exclude='*' ./a/ ./b/
Ryan Williams
fonte
Obrigado. Editou minha resposta com a correção equivalente de ancorar o regex no final da linha ( sub("/[^/]*$")).
Sqweek 21/05/19
0

Tentei com um script c #, pois é a linguagem que eu tenho mais experiência. Eu sou capaz de criar a lista de arquivos que eu quero incluir, mas alguém rsync ainda está me dizendo para fazer uma caminhada. Ele cria as pastas, mas ignora os arquivos. Aqui está o que eu tenho ..

Primeiro o conteúdo do diretório:

~/mono$ ls -l
total 24
drwxr-xr-x 5 me me 4096 Jan 15 00:36 a
drwxr-xr-x 2 me me 4096 Jan 15 00:36 b
drwxr-xr-x 3 me me 4096 Jan 14 00:31 bin
-rw-r--r-- 1 me me 3566 Jan 15 00:31 test.cs
-rwxr-xr-x 1 me me 4096 Jan 15 00:31 test.exe
-rwxr--r-- 1 me me  114 Jan 14 22:40 test.sh

Em seguida, a saída do script C #:

~/mono$ mono test.exe

/a/myfile/myfileseries.pdf
/a/myfile2/testfile.pdf

E a saída de depuração:

~/mono$ mono test.exe | rsync -avvvz --include='*/' --include-from=- --exclude='*' ./a/ ./b/
[client] add_rule(+ */)
[client] parse_filter_file(-,20,3)
[client] add_rule(+ /a/myfile/myfileseries.pdf)
[client] add_rule(+ /a/myfile2/testfile.pdf)
[client] add_rule(- *)
sending incremental file list
[sender] make_file(.,*,0)
[sender] hiding file 1Name.txt because of pattern *
[sender] showing directory myfile2 because of pattern */
[sender] make_file(myfile2,*,2)
[sender] hiding file 1.txt because of pattern *
[sender] hiding file 2.txt because of pattern *
[sender] hiding file Name1.txt because of pattern *
[sender] showing directory test1 because of pattern */
[sender] make_file(test1,*,2)
[sender] hiding file NaMe.txt because of pattern *
[sender] showing directory myfile because of pattern */
[sender] make_file(myfile,*,2)
send_file_list done
send_files starting
[sender] hiding file myfile/myfileseries.pdf because of pattern *
[sender] hiding file myfile2/testfile.pdf because of pattern *
[sender] hiding file test1/test.txt because of pattern *
user1957413
fonte
0

[EDIT] Isso funciona apenas localmente. Para caminhos remotos, a estrutura de diretórios deve ser criada primeiro.

Mais simples que a resposta aceita; Use --file-from, que inclui diretórios pai automaticamente e imprima o caminho do arquivo com% P

find /tmp/source -wholename '*[Nn][Aa][Mm][E]*' -printf '%P\n' | rsync -vzrm --exclude='*/' --files-from=- /tmp/source/ /tmp/target/

Então você só precisa usar finde rsync.

fóbico
fonte