O comando “find” funciona de forma mais eficiente para excluir muitos arquivos?

4

Eu quero excluir arquivos antigos em um diretório que tenha um grande número de arquivos em vários subdiretórios.

Estou tentando usar o seguinte: depois de algum googling, parece ser a maneira recomendada e eficiente:

find . -mindepth 2 -mtime +5 -print -delete

Minha expectativa é que isso imprima um arquivo que satisfaça as condições (modificadas há mais de 5 dias e que satisfaçam a condição de profundidade mínima), exclua-as e passe para o próximo arquivo.

No entanto, como este comando é executado, posso ver que o uso de memória do find está aumentando, mas nada foi impresso (e, portanto, acho que nada foi excluído ainda). Isso parece implicar que find está primeiro coletando todos os arquivos que satisfazem as condições e depois de percorrer toda a árvore do sistema de arquivos, ele irá imprimir e excluir os arquivos.

Existe uma maneira de apagá-lo imediatamente depois de executar os testes no arquivo? Isso ajudaria a fazer a limpeza incrementalmente - eu posso escolher matar o comando e executá-lo novamente mais tarde (o que efetivamente reiniciaria a exclusão de arquivos). Isso não parece acontecer atualmente porque o find não começou a deletar nada até que seja feito a travessia da gigantesca árvore do sistema de arquivos. Existe alguma maneira de contornar isso?

EDITAR - incluindo dados solicitados sobre o meu caso de uso:

Os diretórios que tenho que limpar têm uma profundidade máxima de cerca de 4; arquivos regulares estão presentes apenas na folha do sistema de arquivos. Existem cerca de 600 milhões de arquivos regulares, com os diretórios folha contendo no máximo 5 arquivos. O fan-out do diretório nos níveis mais baixos é de cerca de 3. O fan-out é enorme nos níveis superiores. O espaço total ocupado é de 6,5 TB em um único disco LVM de 7,2 TB (com 4 HDDs físicos de 2 TB)

    
por donatello 28.08.2014 / 01:46

3 respostas

9

A razão pela qual o comando find é lento

Essa é uma questão realmente interessante ... ou, honestamente, mallicious :

O comando

find . -mindepth 2 -mtime +5 -print -delete

é muito diferente da variante de teste usual, deixando de fora a parte perigosa, -delete :

find . -mindepth 2 -mtime +5 -print

A parte complicada é que a ação -delete implica na opção -depth . O comando incluindo delete é realmente

find . -depth -mindepth 2 -mtime +5 -print -delete

e deve ser testado com

find . -depth -mindepth 2 -mtime +5 -print

Isso está intimamente relacionado com os sintomas que você vê; A opção -depth está alterando o algoritmo traversal de árvore para a árvore do sistema de arquivos de um < pesquisa avançada em profundidade na pré-encomenda para uma pesquisa em profundidade na primeira ordem . Antes, cada arquivo ou diretório que era alcançado era imediatamente usado e esquecido. Find estava usando a própria árvore para encontrar o caminho. Agora, find precisará coletar todos os diretórios que podem conter arquivos ou diretórios ainda a serem encontrados, antes de excluir os arquivos nos diretórios mais profundos primeiro . Para isso, ele precisa fazer o trabalho de planejar e lembrar as próprias etapas de passagem e - esse é o ponto - em uma ordem diferente da que a árvore do sistema de arquivos suporta naturalmente. Então, de fato, ele precisa coletar dados em muitos arquivos antes da primeira etapa do trabalho de saída.

O Find precisa controlar alguns diretórios para visitar mais tarde, o que não é um problema para alguns diretórios.
Mas talvez com muitos diretórios, para vários graus de muitos. Além disso, problemas de desempenho fora do alcance serão notados nesse tipo de situação; Então, é possível que nem mesmo find seja lento, mas algo mais.

O impacto no desempenho e na memória depende da sua estrutura de diretórios, etc.


As seções relevantes de man find :

Veja os "Avisos":

ACTIONS
    -delete
           Delete  files;  true if removal succeeded.  If the removal failed,
           an error message is issued.  If -delete fails, find's exit  status
           will  be nonzero (when it eventually exits).  Use of -delete auto‐
           matically turns on the -depth option.

           Warnings: Don't forget that the find command line is evaluated  as
           an  expression,  so  putting  -delete  first will make find try to
           delete everything below the starting points you  specified.   When
           testing  a  find  command  line  that you later intend to use with
           -delete, you should explicitly specify -depth in  order  to  avoid
           later  surprises.  Because -delete implies -depth, you cannot use‐
           fully use -prune and -delete together.
    [ ... ]

E, de uma seção adiante:

 OPTIONS
    [ ... ]
    -depth Process each directory's contents  before  the  directory  itself.
           The -delete action also implies -depth.


A solução mais rápida para excluir os arquivos

Você realmente não precisa excluir os diretórios na mesma execução de excluir os arquivos, certo? Se não estamos excluindo diretórios, não precisamos de todo o -depth , podemos apenas encontrar um arquivo e excluí-lo, e passar para o próximo, como você propôs.

Desta vez, podemos usar a variante de impressão simples para testar o find , com -print implícito.

Queremos encontrar apenas arquivos simples, sem links simbólicos, diretórios, arquivos especiais, etc:

find . -mindepth 2 -mtime +5 -type f

Usamos xargs para excluir mais de um arquivo por rm processo iniciado, cuidando de nomes de arquivos ímpares usando um byte nulo como separador:

Testando este comando - observe o echo na frente do rm , então ele imprime o que será executado posteriormente:

find . -mindepth 2 -mtime +5 -type f -print0 | xargs -0 echo rm

As linhas serão muito longas e difíceis de ler; Para um teste inicial, pode ajudar a obter resultados legíveis com apenas três arquivos por linha, adicionando -n 3 como os primeiros argumentos de xargs

Se tudo estiver bem, remova o echo na frente do rm e execute novamente.

Isso deve ser muito mais rápido ;

No caso de estarmos falando de milhões de arquivos - você escreveu 600 milhões de arquivos no total - há algo mais a ser levado em conta:

A maioria dos programas, incluindo find , lê diretórios usando a chamada da biblioteca readdir (3) . Isso geralmente usa um buffer de 32 KB para ler diretórios; Isso se torna um problema quando os diretórios, contendo listas enormes de nomes de arquivos possivelmente longos, são grandes.

A maneira de contornar isso é usar diretamente a chamada do sistema para ler entradas de diretório, getdents (2) , e manipular o buffer de uma forma mais adequada.

Para detalhes, consulte Você pode listar um diretório contendo 8 milhões de arquivos! Mas não com ls ..

(Seria interessante se você pudesse adicionar detalhes à sua pergunta sobre os números típicos de arquivos por diretórios, diretórios por diretório, profundidade máxima de caminhos; Além disso, qual sistema de arquivos é usado.)

(Se ainda estiver lento, você deve verificar os problemas de desempenho do sistema de arquivos.)

    
por 28.08.2014 / 06:50
0

Sempre gostei de usar a opção -exec :

find . -mindepth 2 -mtime +5 -type f -exec rm -f {} \;

O -v após rm imprimirá o arquivo que será excluído, mas exibir a saída no console parece atrasar um pouco as coisas, portanto, se a velocidade for o requisito, deixarei isso de lado.

    
por 28.08.2014 / 08:27
0

Muito mais rápido para usar o rsync com a opção --delete . Basta ter uma pasta vazia para sincronizar com a pasta de destino e viola tudo foi super rápido. O comando rm -rf é lento, pois verifica cada inode para vincular antes de remover.

    
por 05.04.2016 / 10:38

Tags