Dividindo uma árvore de diretórios grande por tipo de arquivo

3

Eu tenho um grande diretório de dados (20-30Gb) na minha máquina de desktop Ubuntu 10.10 que consiste em muitos arquivos de dados brutos, arquivos de dados processados e scripts, tabelas, figuras etc. gerados a partir dos dados processados. O diretório de dados acumulou-se ao longo de muitos anos e está muito mal estruturado - "um dia" vou resolver, mas há sempre coisas mais importantes a fazer.

Agora estou mudando para um serviço de backup on-line e, para reduzir o tempo necessário para o backup e o armazenamento on-line, gostaria de dividir os dados brutos, que ocupam muito espaço, mas é facilmente substituído como já está arquivado em outro lugar, mantendo sua posição geral na estrutura de diretórios. Em outras palavras, eu quero ir de algo como:

/data/A/raw1.data
/data/A/raw2.data
/data/A/raw3.data
/data/A/processed.txt
/data/A/figure.eps
/data/A/plot.gnu
/data/B/raw4.data
/data/B/processed.txt
... etc.

para

/data/A/processed.txt
/data/A/figure.eps
/data/A/plot.gnu
/data/B/processed.txt
... etc.

e

/raw_data/A/raw1.data
/raw_data/A/raw2.data
/raw_data/A/raw3.data
/raw_data/B/raw4.data
... etc.

Assim, os arquivos de dados brutos trocam de / data para / raw_data, mas mantêm suas posições na estrutura de diretórios, enquanto os dados processados e os arquivos associados permanecem no mesmo lugar. A estrutura geral do arquivo é muito mais complexa e desordenada do que isso, mas a graça salvadora é que todos os dados brutos podem ser identificados por tipo de arquivo (principalmente .fits e .sdf).

Tenho certeza que isso é trivial com a combinação certa de comandos e / ou algumas linhas de script bash, mas meu conhecimento de linha de comando é limitado ao básico e eu prefiro perguntar do que arriscar bagunçar tudo:)

E, como um aparte, existe uma maneira simples de procurar duplicatas nos dados brutos - terá nome de arquivo e tamanho idênticos, não necessariamente timestamp que é redefinido à medida que os dados são baixados do arquivo, apesar de estar completamente certo Preciso canalizar cada candidato duplicado por meio de dfits e grep o registro de data e hora no cabeçalho de ajustes.

    
por strmqm 02.08.2011 / 09:39

2 respostas

4

Uma maneira de fazer isso seria usar rsync com algumas regras de inclusão / exclusão especialmente criadas e a opção de remover os arquivos de origem após a sincronização, desta forma:

rsync -av --include "*/" --include='*.fits' --include='*.sdf' \
    --exclude='*' --remove-source-files /data/ /raw_data/

Se você quiser avançar passo a passo em um loop para poder incluir outras ações, precisará de um script que faça algo assim:

DIR1="/data"
DIR2="/raw_data"

find "$DIR1" -type f \( -iname '*.fits' -or -iname '*.sdf' \) -print0 |
    while read -d $'
rsync -av --include "*/" --include='*.fits' --include='*.sdf' \
    --exclude='*' --remove-source-files /data/ /raw_data/
' file; do mkdir -p "$DIR2/$(basename "$file")" mv "$file" "$DIR2/$(basename "$file")" done
    
por 02.08.2011 / 10:32
3

Existem várias ferramentas de cópia de arquivos que permitem construir um nome de diretório de destino com regras suficientemente flexíveis ( zcp , rsync , pax ,…). Infelizmente, poucos deles permitem tanto mover (ao contrário de copiar) e criar diretórios de destino sob demanda. Então, mostrarei algumas maneiras de fazer isso em duas etapas: primeiro crie todos os diretórios de destino potencialmente necessários e, em seguida, execute a movimentação.

Renomear Perl

O programa Perl rename fornecido pela Debian e pelo Ubuntu pode criar o diretório de destino quando necessário, se você escrever o bit de Perl necessário.

shopt -s globstar       # make **/ traverse directories recursively (requires bash 4)
rename 'BEGIN {use File::Path}
        s!^/data!/raw_data!;
        m!(.*)/!; mkpath($1)' /data/**/*.raw

Em zsh, omita a linha shopt -s globstar ; ** significa percurso recursivo por padrão. Em shells diferentes de bash e zsh, você precisa usar find para travessias recursivas (veja os exemplos abaixo). Não se preocupe com tudo isso se você tiver um único nível de diretórios.

Criando os diretórios de destino

Em zsh (explicação: o qualificador de glob / significa corresponder apenas diretórios e o qualificador e glob aplica a transformação fornecida posteriormente a cada nome):

mkdir /data/**/*(/e\''REPLY=${REPLY/data/raw_data}'\')

Com outros shells:

find /data -type d \
     -exec sh -c 'for d; do mkdir "/raw_data${d#/data}"; done' _ {} +

Se você tiver apenas um nível de subdiretórios, é muito mais simples:

for d in /data/*/; do mkdir "/raw_data${d#/data}"; done

Movendo os arquivos (zsh)

autoload zmv
zmv -Q '/data/(**/)(*.raw)(.)' '/raw_data/$1$2'

Movendo os arquivos (portáteis)

find /data -name '*.raw' \
     -exec sh -c 'for x; do mv "$x" "/raw_data${x#/data}"; done' _ {} +
    
por 03.08.2011 / 01:55