Exclua com eficiência o diretório grande que contém milhares de arquivos


162

Temos um problema com uma pasta que se torna pesada com centenas de milhares de arquivos minúsculos.

Existem tantos arquivos que executar rm -rfretornam um erro e, em vez disso, o que precisamos fazer é algo como:

find /path/to/folder -name "filenamestart*" -type f -exec rm -f {} \;

Isso funciona, mas é muito lento e constantemente falha ao ficar sem memória.

Existe uma maneira melhor de fazer isso? Idealmente, eu gostaria de remover o diretório inteiro sem me preocupar com o conteúdo dele.


16
rm -rf *na pasta provavelmente falha por causa de muitos argumentos; mas e rm -rf folder/se você quiser remover todo o diretório?
sr_ 26/04/12

4
Em vez de excluí-lo manualmente, sugiro ter a pasta em uma partição separada e simplesmente desmontar && format && remontar.
precisa saber é o seguinte

7
Apenas por curiosidade - quantos arquivos são necessários para quebrar rm -rf?
Jw013

7
Você provavelmente deve renomear a pergunta para algo mais preciso, como "Exclua com eficiência um diretório grande que contém milhares de arquivos". Para excluir um diretório e seu conteúdo, a recursão é necessária por definição. Você pode desvincular manualmente apenas o próprio inode do diretório (provavelmente requer privilégios de root), desmontar o sistema de arquivos e executá fsck-lo para recuperar os blocos de disco não utilizados, mas essa abordagem parece arriscada e pode não ser mais rápida. Além disso, a verificação do sistema de arquivos pode envolver percorrer recursivamente a árvore do sistema de arquivos.
jw013

4
Uma vez que eu tinha uma ccacheárvore de arquivos tão grande e rmdemorava tanto (e tornando o sistema lento), era consideravelmente mais rápido copiar todos os outros arquivos do sistema, formatar e copiá-los novamente. Desde então, eu dou a essas árvores de arquivos pequenos e enormes o seu próprio sistema de arquivos dedicado, para que você possa mkfsdiretamente em vez de rm.
Frostschutz 15/06/2013

Respostas:


213

Usar o rsync é surpreendente, rápido e simples.

mkdir empty_dir
rsync -a --delete empty_dir/    yourdirectory/

A resposta do @ sarath mencionou outra opção rápida: Perl! Seus benchmarks são mais rápidos que rsync -a --delete.

cd yourdirectory
perl -e 'for(<*>){((stat)[9]<(unlink))}'

Fontes:

  1. https://stackoverflow.com/questions/1795370/unix-fast-remove-directory-for-cleaning-up-daily-builds
  2. http://www.slashroot.in/which-is-the-fastest-method-to-delete-files-in-linux

4
Obrigado, muito útil. Eu uso o rsync o tempo todo, não fazia ideia de que você poderia usá-lo para excluir assim. Muito mais rápido do que rm-rf
John Powell

22
rsyncpode ser mais rápido que o normal rm, porque garante as exclusões na ordem correta, portanto, é necessária menos recomputação de btress. Veja esta resposta serverfault.com/a/328305/105902
Marki555

7
Alguém pode modificar a expressão perl para excluir recursivamente todos os diretórios e arquivos dentro de um directory_to_be_deleted ?
Abhinav

5
Notas: adicione a -Popção ao rsync para mais exibição, tenha cuidado com a sintaxe, as barras finais são obrigatórias. Finalmente, você pode iniciar o comando rsync pela primeira vez com a -nopção primeiro de iniciar uma execução a seco .
Drasill

1
-aiguais -rlptgoD, mas somente para exclusão -rdé necessário
Koen.

38

Alguém no Twitter sugeriu usar em -deletevez de-exec rm -f{} \;

Isso melhorou a eficiência do comando, mas ainda usa recursão para passar por tudo.


11
Isso não é padrão. GNU findtem -delete, e outros findtalvez.
enzotib

13
-deletesempre deve ser preferido -exec rmquando disponível, por razões de segurança e eficiência.
Jw013

6
GNU é o padrão de fato .
precisa saber é o seguinte

17

Que tal algo como: find /path/to/folder -name "filenamestart*" -type f -print0 | xargs -0rn 20 rm -f

Você pode limitar o número de arquivos a serem excluídos de uma só vez, alterando o argumento para o parâmetro -n. Os nomes dos arquivos com espaços em branco também estão incluídos.


2
Você provavelmente não precisa do -n 20bit, pois o xargs deve se limitar a tamanhos aceitáveis ​​de lista de argumentos.
Inutil)

Sim você está certo. Aqui está uma nota man xargs: (...) max-chars characters per command line (...). The largest allowed value is system-dependent, and is calculated as the argument length limit for exec. Portanto, a -nopção é para os casos em que o xargs não pode determinar o tamanho do buffer da CLI ou se o comando executado possui alguns limites.
digital_infinity

12

Um truque inteligente:

rsync -a --delete empty/ your_folder/

É super intensivo em CPU, mas realmente muito rápido. Consulte https://web.archive.org/web/20130929001850/http://linuxnote.net/jianingy/en/linux/a-fast-way-to-remove-huge-number-of-files.html


Não é tão rápido, porque lê o conteúdo do diretório de maneira eficiente. Veja esta resposta para 10x mais rápido solução e explicação serverfault.com/a/328305/105902
Marki555

2
@ Marki555: na edição da pergunta, são relatados 60 segundos para rsync -a --deletevs 43 para lsdent. A razão 10x foi para time ls -1 | wc -l vs time ./dentls bigfolder >out.txt(que é uma comparação parcialmente justa por causa de > filevs wc -l).
Hastur 21/01

O problema é que NENHUM dos comandos lá realmente FAZER a operação de travessia desejado para exclusão. O código que eles dão? NÃO FUNCIONA como descrito por Marki555.
Svartalf 10/09

11

Expandindo um dos comentários, não acho que você esteja fazendo o que pensa que está fazendo.

Primeiro, criei uma enorme quantidade de arquivos para simular sua situação:

$ mkdir foo
$ cd foo/
$ for X in $(seq 1 1000);do touch {1..1000}_$X; done

Depois, tentei o que esperava falhar e o que parece que você está fazendo na pergunta:

$ rm -r foo/*
bash: /bin/rm: Argument list too long

Mas isso faz o trabalho:

$ rm -r foo/
$ ls foo
ls: cannot access foo: No such file or directory

6
Esta é a única solução que funcionou: Execute rm -Rf bigdirectoryvárias vezes. Eu tinha um diretório com milhares de milhões de subdiretórios e arquivos. Eu não poderia mesmo funcionar lsou findou rsyncnesse diretório, porque ele ficou sem memória. O comando é rm -Rffechado várias vezes (sem memória), excluindo apenas parte dos bilhões de arquivos. Mas depois de muitas tentativas, ele finalmente fez o trabalho. Parece ser a única solução se ficar sem memória o problema.
11114 erik

6

Tive a oportunidade de testar -deleteem comparação com -exec rm \{\} \;e para mim -deletefoi a resposta para esse problema.

O uso de -deletearquivos excluídos em uma pasta de 400.000 arquivos pelo menos 1.000 vezes mais rápido que rm.

O artigo 'Como excluir grande número de arquivos no Linux' sugere que é cerca de três vezes mais rápido, mas no meu teste a diferença foi muito mais dramática.


3
Usar find -execexecuta o rmcomando para cada arquivo separadamente, é por isso que é tão lento.
Marki555

5

Sobre a -deleteopção acima: Estou usando-o para remover um grande número de arquivos (1M + est) em uma pasta temporária que eu criei e esqueci inadvertidamente a limpeza noturna. Enchi meu disco / partição acidentalmente e nada mais poderia removê-los, a não ser o find .comando. Está lento, no começo eu estava usando:

find . -ls -exec rm {} \;

Mas isso levou um tempo EXTREMO. Ele foi iniciado após cerca de 15 minutos para remover alguns arquivos, mas meu palpite é que ele estava removendo menos de 10 ou mais por segundo depois que finalmente foi iniciado. Então, eu tentei o:

find . -delete

em vez disso, e estou deixando isso funcionar agora. Parece estar rodando mais rápido, apesar de estar EXTREMAMENTE sobrecarregando a CPU, o que o outro comando não estava. Está em execução há mais de uma hora e acho que estou recuperando espaço na minha unidade e a partição gradualmente "diminuindo", mas ainda está demorando muito tempo. Eu duvido seriamente que esteja rodando 1.000 vezes mais rápido que o outro. Como em todas as coisas, eu só queria apontar a troca no espaço versus tempo. Se você tiver a largura de banda da CPU de sobra (temos), execute a última. A minha CPU está funcionando ( uptimerelatórios):

10:59:17 up 539 days, 21:21,  3 users,  load average: 22.98, 24.10, 22.87

E já vi a média de carga ultrapassar as 30,00, o que não é bom para um sistema ocupado, mas para o nosso, que normalmente é carregado com pouca carga, fica bom por algumas horas. Eu verifiquei a maioria das outras coisas no sistema e elas ainda respondem, por isso estamos bem por enquanto.


se você vai usar, execcertamente não quer usar -lse o find . -type f -exec rm '{}' ++ é mais rápido, pois dará tantos argumentos para a rm quanto ele pode lidar de uma só vez.
Xenoterracide

Eu acho que você deve ir em frente e editar isso em sua própria resposta ... é realmente muito longo para um comentário. Além disso, parece que seu sistema de arquivos possui exclusões bastante caras, curioso qual é? Você pode executar esse find … -deletemeio niceou ionice, que podem ajudar. Portanto, pode alterar algumas opções de montagem para configurações menos seguras contra falhas. (E, claro, dependendo do que o resto é no sistema de arquivos, a maneira mais rápida de eliminar tudo é muitas vezes mkfs.)
derobert

3
A média de carga nem sempre é a CPU, é apenas uma medida do número de processos bloqueados ao longo do tempo. Os processos podem bloquear a E / S do disco, o que provavelmente é o que está acontecendo aqui.
Score_Under

Observe também que a média de carga não é responsável pelo número de CPUs lógicas. Portanto, loadavg 1para máquina de núcleo único é o mesmo que loadavg 64no sistema de 64 núcleos - o que significa que cada CPU está ocupada 100% do tempo.
Marki555


3

Considere usar o volume Btrfs e simplesmente exclua o volume inteiro desse diretório com grande número de arquivos.

Como alternativa, você pode criar um arquivo de imagem FS, desmontar e excluir o arquivo para remover tudo de uma só vez muito rápido.


2

Supondo que o GNU esteja parallelinstalado, eu usei isso:

parallel rm -rf dir/{} ::: `ls -f dir/`

e foi rápido o suficiente.


1

A exclusão de diretórios REALMENTE GRANDES precisa de uma abordagem diferente, como aprendi neste site - você precisará utilizar o ionice. Ele garante (com -c3) que exclusões somente serão executadas quando o sistema tiver tempo de IO para isso. A carga de seus sistemas não aumentará muito e tudo permanecerá responsivo (embora o tempo de busca da CPU tenha sido bastante alto em cerca de 50%).

find <dir> -type f -exec ionice -c3 rm {} \;

5
utilizando +, em vez de \;se fazer isso mais rápido que passa mais argumentos para rm ao mesmo tempo, menos bifurcação
xenoterracide

1
Por que não ionice -c3 find <dir> -type f -delete
Jtgd 27/10

0
ls -1 | xargs rm -rf 

deve funcionar dentro da pasta principal


1
lsnão funcionará devido à quantidade de arquivos na pasta. É por isso que eu tive que usar find, obrigado.
26412 Toby

4
@ Toby: Tente ls -f, o que desativa a classificação. A classificação requer que todo o diretório seja carregado na memória para ser classificado. Um não classificado lsdeve ser capaz de transmitir sua saída.
CAMH

1
Não funciona em nomes de arquivos que contêm novas linhas.
maxschlepzig

@camh isso é verdade. Porém, a remoção de arquivos na ordem classificada é mais rápida do que na não classificada (devido ao recálculo da btree do diretório após cada exclusão). Veja esta resposta para um exemplo serverfault.com/a/328305/105902
Marki555

@maxschlepzig para esses arquivos que você pode usar find . -print0 | xargs -0 rm, que usará o caractere NULL como separador de nome de arquivo.
Marki555

0

Para a dica de Izkata acima:

Mas isso faz o trabalho:

$ rm -r foo/
$ ls foo
ls: cannot access foo: No such file or directory

Isso quase funcionou - ou teria funcionado - mas eu tive alguns problemas de permissão; os arquivos estavam em um servidor, mas ainda não entendo de onde veio esse problema de permissão. De qualquer forma, o Terminal pediu confirmação em todos os arquivos. A quantidade de arquivos era de cerca de 20.000, portanto não era uma opção. Depois de "-r", adicionei a opção "-f", então todo o comando foi " rm -r -f foldername / ". Então pareceu funcionar bem. Sou iniciante no Terminal, mas acho que tudo bem, certo? Obrigado!


0

Dependendo de quão bem você precise se livrar desses arquivos, sugiro usá-lo shred.

$ shred -zuv folder

se você deseja limpar o diretório, mas não pode removê-lo e recriá-lo, sugiro movê-lo e recriá-lo instantaneamente.

mv folder folder_del
mkdir folder
rm -rf folder_del

isso é mais rápido, acredite ou não, pois apenas um inode deve ser alterado. Lembre-se: você não pode realmente paralelizar essa prova em um computador com vários núcleos. Tudo se resume ao acesso ao disco, limitado pelo RAID ou pelo que você possui.


1
shred não funcionará com muitos sistemas de arquivos modernos.

0

Se você possui milhões de arquivos e todas as soluções acima colocam seu sistema em estresse, tente esta inspiração:

Arquivo nice_delete:

#!/bin/bash

MAX_LOAD=3
FILES=("$@")
BATCH=100

while [ ${#FILES[@]} -gt 0 ]; do
    DEL=("${FILES[@]:0:$BATCH}")
    ionice -c3 rm "${DEL[@]}"
    echo -n "#"
    FILES=("${FILES[@]:$BATCH}")
    while [[ $(cat /proc/loadavg | awk '{print int($1)}') -gt $MAX_LOAD ]]; do
        echo -n "."
        sleep 1
    done
done

E agora exclua os arquivos:

find /path/to/folder -type f -exec ./nice_delete {} \+

O Find criará lotes (consulte getconf ARG_MAX) de algumas dezenas de milhares de arquivos e os passará para nice_delete. Isso criará lotes ainda menores para permitir suspensão quando a sobrecarga for detectada.


0

Se você quiser se livrar de muitos arquivos o mais rápido possível, ls -f1 /path/to/folder/with/many/files/ | xargs rmpode funcionar bem, mas é melhor não executá-lo nos sistemas de produção, pois seu sistema pode se tornar um problema de E / S e os aplicativos podem ficar presos durante a operação de exclusão.

Esse script funciona bem para muitos arquivos e não deve afetar o ioload do sistema.

#!/bin/bash

# Path to folder with many files
FOLDER="/path/to/folder/with/many/files"

# Temporary file to store file names
FILE_FILENAMES="/tmp/filenames"

if [ -z "$FOLDER" ]; then
    echo "Prevented you from deleting everything! Correct your FOLDER variable!"
    exit 1
fi

while true; do
    FILES=$(ls -f1 $FOLDER | wc -l)
    if [ "$FILES" -gt 10000 ]; then
        printf "[%s] %s files found. going on with removing\n" "$(date)" "$FILES"
        # Create new list of files
        ls -f1 $FOLDER | head -n 5002 | tail -n 5000 > "$FILE_FILENAMES"

        if [ -s $FILE_FILENAMES ]; then
            while read FILE; do
                rm "$FOLDER/$FILE"
                sleep 0.005
            done < "$FILE_FILENAMES"
        fi
    else
        printf "[%s] script has finished, almost all files have been deleted" "$(date)"
        break
    fi
    sleep 5
done
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.