Copiando diretórios recursivamente com um nome

0

Com uma estrutura de diretórios criada usando

#!/bin/bash
for name in A B
do
    mkdir -p /tmp/src/${name}/copyme
    echo "do not copy" > /tmp/src/${name}/no.txt
    echo "copy" > /tmp/src/${name}/copyme/yes.txt
done

Gostaria de espelhar apenas os diretórios copyme , juntamente com os arquivos dentro deles, para / tmp / tgt.

Isso deve ser simples. Baseando-se em rsync distinguindo a ordem das opções de linha de comando: exclude everything e inclua o padrão relevante. Ainda

rsync -av --exclude='*' --include='copyme' /tmp/src/ /tmp/tgt/

exclui tudo (somente o diretório de destino é criado). Por quê?

    
por Calaf 12.06.2018 / 12:29

1 resposta

0

Quando rsync é executado, ele testa os nomes encontrados na origem em relação aos padrões e o primeiro padrão correspondente entra em vigor:

$ rsync -avv --exclude='*' --include='copyme' /tmp/src/ /tmp/tgt/
sending incremental file list
[sender] hiding directory A because of pattern *
[sender] hiding directory B because of pattern *
delta-transmission disabled for local transfer or --whole-file
total: matches=0  hash_hits=0  false_alarms=0 data=0

sent 51 bytes  received 86 bytes  274.00 bytes/sec
total size is 0  speedup is 0.00

Quando um diretório foi excluído (veja "ocultando diretório ..." acima), seu conteúdo não é mais considerado. A reversão dos padrões de exclusão e inclusão não ajudará, pois nunca chegará aos diretórios copyme .

O manual rsync diz:

For instance, to include /foo/bar/baz, the directories /foo and /foo/bar must not be excluded. Excluding one of those parent directories prevents the examination of its content, cutting off rsync's recursion into those paths and rendering the include for /foo/bar/baz ineffectual (since rsync can't match something it never sees in the cut-off section of the directory hierarchy).

Então, ao invés disso:

$ rsync -avv --include='[AB]' --include='copyme/***' --exclude='*' /tmp/src/ /tmp/tgt/
sending incremental file list
[sender] showing directory A because of pattern [AB]
[sender] showing directory B because of pattern [AB]
[sender] showing directory A/copyme because of pattern copyme/***
[sender] hiding file A/no.txt because of pattern *
[sender] showing file A/copyme/yes.txt because of pattern copyme/***
[sender] showing directory B/copyme because of pattern copyme/***
[sender] hiding file B/no.txt because of pattern *
[sender] showing file B/copyme/yes.txt because of pattern copyme/***
created directory /tmp/tgt
delta-transmission disabled for local transfer or --whole-file
./
A/
A/copyme/
A/copyme/yes.txt
B/
B/copyme/
B/copyme/yes.txt
total: matches=0  hash_hits=0  false_alarms=0 data=10

sent 305 bytes  received 175 bytes  960.00 bytes/sec
total size is 10  speedup is 0.02

Note que a exclusão tem que vir por último, após as inclusões. O padrão copyme/*** corresponde ao nome do diretório copyme e a qualquer nome de caminho abaixo dele.

Se você não quiser codificar os nomes dos diretórios A e B :

for dir in /tmp/src/*; do
    [ ! -d "$dir" ] && continue
    rsync -avv --include="${dir##*/}" --include='copyme/***' --exclude='*' /tmp/src/ /tmp/tgt/
done

Isso produziria

sending incremental file list
[sender] showing directory A because of pattern A
[sender] hiding directory B because of pattern *
[sender] showing directory A/copyme because of pattern copyme/***
[sender] hiding file A/no.txt because of pattern *
[sender] showing file A/copyme/yes.txt because of pattern copyme/***
created directory /tmp/tgt
delta-transmission disabled for local transfer or --whole-file
./
A/
A/copyme/
A/copyme/yes.txt
total: matches=0  hash_hits=0  false_alarms=0 data=5

sent 180 bytes  received 148 bytes  656.00 bytes/sec
total size is 5  speedup is 0.02
sending incremental file list
[sender] hiding directory A because of pattern *
[sender] showing directory B because of pattern B
[sender] showing directory B/copyme because of pattern copyme/***
[sender] hiding file B/no.txt because of pattern *
[sender] showing file B/copyme/yes.txt because of pattern copyme/***
delta-transmission disabled for local transfer or --whole-file
B/
B/copyme/
B/copyme/yes.txt
total: matches=0  hash_hits=0  false_alarms=0 data=5

sent 180 bytes  received 117 bytes  594.00 bytes/sec
total size is 5  speedup is 0.02

O resultado seria

$ tree src tgt
src
|-- A
|   |-- copyme
|   |   '-- yes.txt
|   '-- no.txt
'-- B
    |-- copyme
    |   '-- yes.txt
    '-- no.txt

4 directories, 4 files
tgt
|-- A
|   '-- copyme
|       '-- yes.txt
'-- B
    '-- copyme
        '-- yes.txt

4 directories, 2 files

Outra abordagem que não usa padrões de exclusão ou inclusão com rsync , mas usa find para localizar os diretórios copyme e, em seguida, rsync para copiá-los:

find /tmp/src -type d -name 'copyme' -prune -exec sh -c '
    cd /tmp/src && rsync -aRvv "${1#/tmp/src/}/" /tmp/tgt/' sh {} ';'

Observe o sinalizador -R ( --relative ) usado com rsync aqui.

O script sh -c executado para cada diretório copyme encontrado faz um cd para /tmp/src e copia o nome do caminho com o bit /tmp/src inicial de seu caminho removido.

O -prune no comando find para find de procurar por mais diretórios copyme dentro do diretório encontrado.

    
por 12.06.2018 / 13:33