Exclua eficientemente o diretório grande contendo milhares de arquivos

139

Temos um problema com uma pasta tornando-se incômoda com centenas de milhares de arquivos minúsculos.

Existem tantos arquivos que executar rm -rf retorna 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 na falta de 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 dentro dele.

    
por Toby 26.04.2012 / 09:50

16 respostas

177

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

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

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

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

Fontes:

  1. link
  2. link
por 17.06.2013 / 09:26
37

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

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

    
por 26.04.2012 / 10:18
17

E 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 do parâmetro -n . Os nomes dos arquivos com espaços em branco também são incluídos.

    
por 26.04.2012 / 10:20
11

Expandindo um dos comentários, não acho que você esteja fazendo o que acha 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

Então eu tentei o que eu esperava falhar e o que parece que você está fazendo na pergunta:

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

Mas isso funciona :

$ rm -r foo/
$ ls foo
ls: cannot access foo: No such file or directory
    
por 26.04.2012 / 16:04
10

Um truque inteligente:

rsync -a --delete empty/ your_folder/

É super intensivo em CPU, mas muito rápido. Veja link

    
por 31.08.2013 / 06:13
6

Eu tive a oportunidade de testar -delete em comparação a -exec rm \{\} \; e, para mim, -delete foi a resposta para esse problema.

Usar -delete excluiu os arquivos em uma pasta de 400.000 arquivos pelo menos 1.000 vezes mais rápido que rm .

O artigo "Como excluir um 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.

    
por 02.07.2013 / 15:17
3

Existem alguns métodos que podem ser usados para excluir um grande número de arquivos no linux. Você pode usar a opção find with delete, que é mais rápida que a opção exec. Então você pode usar perl unlink, então até rsync. Como excluir um grande número de arquivos no linux

    
por 15.06.2013 / 13:39
3

Considere o uso do volume Btrfs e simplesmente exclua todo o volume de um diretório com um grande número de arquivos.

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

    
por 27.02.2017 / 16:46
2

Sobre a opção -delete acima: Estou a usá-la para remover um grande número de arquivos (1M + est) em uma pasta temporária que criei e, inadvertidamente, esqueci de limpar todas as noites. Eu preenchi meu disco / partição acidentalmente, e nada mais poderia removê-los, exceto o comando find . . É lento, no começo eu estava usando:

find . -ls -exec rm {} \;

Mas isso estava levando uma quantia EXTREMA de tempo. Ele começou após cerca de 15 minutos para remover alguns dos arquivos, mas meu palpite é que ele estava removendo menos de 10 por segundo depois que finalmente começou. Então, eu tentei o:

find . -delete

em vez disso, e eu estou deixando isso acontecer agora. Parece estar correndo mais rápido, embora esteja extremamente sobrecarregando a CPU que o outro comando não era. Ele está funcionando há uma hora e acho que estou recuperando espaço no drive e a partição está gradualmente diminuindo, mas ainda está demorando muito. Eu duvido seriamente que ele 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ê tem a largura de banda da CPU de sobra (nós), então execute o último. Ele está com minha CPU em execução ( uptime reports):

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

E eu vi a média de carga passar de 30,00, o que não é bom para um sistema ocupado, mas para o nosso, que normalmente é levemente carregado, tudo bem por algumas horas. Eu verifiquei a maioria das outras coisas no sistema e elas ainda estão respondendo, então estamos bem por enquanto.

    
por 31.12.2013 / 20:00
1

Excluir diretórios REALMENTE GRANDES precisa de uma abordagem diferente, como aprendi em este site - você vai precisar utilizar ionice.Ele garante (com -c3) que as exclusões só serão realizadas quando o sistema tem IO-time para ele. Sua carga de sistemas não aumentará e tudo permanecerá responsivo (embora o tempo de processamento da CPU tenha sido bastante alto em cerca de 50%).

find <dir> -type f -exec ionice -c3 rm {} \;
    
por 10.05.2013 / 08:51
1

Supondo que o GNU parallel esteja instalado, usei isto:

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

e foi rápido o suficiente.

    
por 03.10.2017 / 02:41
0
ls -1 | xargs rm -rf 

deve funcionar dentro da pasta principal

    
por 26.04.2012 / 10:17
0

Para a sugestão de Izkata acima:

But this does work:

$ 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 com permissão; os arquivos estavam em um servidor, mas ainda não entendi de onde veio esse problema de permissão. De qualquer forma, o Terminal pediu confirmação em todos os arquivos. Quantidade de arquivos foi de cerca de 20 000, então isso não era uma opção. Depois de "-r" eu adicionei a opção "-f", então o comando inteiro era " rm -rf / ". Então pareceu funcionar bem. Eu sou um novato com Terminal, mas acho que isso foi bem, certo? Obrigado!

    
por 20.06.2013 / 07:42
0

Dependendo de quão bem você precisa se livrar desses arquivos, sugiro usar shred .

$ shred -zuv folder

Se você quiser limpar o diretório, mas não puder 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, já que apenas um inode precisa ser mudado. Lembre-se: você não pode realmente fazer um paralelismo com esse recurso em um computador multicore. Tudo se resume ao acesso ao disco, que é limitado pelo RAID ou o que você tem.

    
por 02.07.2013 / 15:56
0

Se você tiver milhões de arquivos e todas as soluções acima estiverem com o sistema em estresse, tente essa 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 (veja getconf ARG_MAX ) de algumas dezenas de milhares de arquivos e passará para nice_delete . Isso criará lotes ainda menores para permitir a suspensão quando a sobrecarga for detectada.

    
por 28.09.2018 / 01:35
0

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

Este script funciona muito 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
    
por 10.11.2018 / 13:24