Excluindo milhões de arquivos

38

Eu tinha um dir preenchido com milhões de imagens gif. Muitos para o comando rm.

Eu tenho tentado o comando find assim:

find . -name "*.gif" -print0 | xargs -0 rm

O problema é que ele atrapalha muito minha máquina e causa tempos limite para os clientes, pois é um servidor.

Existe alguma maneira mais rápida de excluir todos esses arquivos ... sem travar a máquina?

Corepuncher
fonte
Estou com uma taxa de exclusão de cerca de 6 gb / h, usando o comando "nice find" abaixo. Provavelmente levará 48 horas para se livrar de todos os arquivos. A razão pela qual isso aconteceu foi que o script de verificação b / ca falhou. "horizonte de eventos" com o comando rm, em seguida, ele fugiu.
3
Remover o diretório inteiro não seria substancialmente mais rápido? Basta tirar os "bons" os arquivos antes de nuking os restantes ...
tucuxi
Bem, todo arquivo está ruim agora, porque foi movido para / dir_old, e eu refiz o / dir. Mas o rmdir não terá a mesma limitação que o rm *?
@ Corepuncher: Eu esperaria que a remoção de todo o diretório (como rm -rfseria mais rápido. Vale a pena tentar.
Jason R
Atualmente, estou executando "rm -rf" no diretório Está em execução há mais de 20 minutos agora ... nenhuma alteração no tamanho do disco ainda. Mas também não retornou automaticamente a "lista de argumentos muito longa". O único problema é que ele está realmente martelando minha máquina e tornando outras coisas lentas / falhas. Não tenho certeza quanto tempo para deixá-lo ir.

Respostas:

44

Mais rápido não é necessariamente o que você deseja. Você pode realmente executar mais devagar , portanto, a exclusão consome menos recursos enquanto está em execução.

Use nice (1) para diminuir a prioridade de um comando.

nice find . -name "*.gif" -delete

Para processos ligados a E / S, nice (1) pode não ser suficiente. O planejador do Linux leva em consideração a E / S, não apenas a CPU, mas você pode desejar um controle mais preciso sobre a prioridade de E / S.

ionice -c 2 -n 7 find . -name "*.gif" -delete

Se isso não acontecer, você também pode adicionar um sono para diminuir a velocidade.

find . -name "*.gif" -exec sleep 0.01 \; -delete
John Kugelman apoia Monica
fonte
3
uau ... milhões de arquivos com um sono de .1 s ... precisam de um dia para 864000 arquivos.
glglgl
7
@glglgl Tudo bem, espertinho. Eu mudei o tempo limite. :-P
John Kugelman apoia Monica
28
O sono pode ser uma boa escolha, mas não é bom, pois a tarefa aqui é vinculada a E / S, não a CPU; você pode tentar o ionice. Observe que, se o sono for muito pequeno, será inútil.
Matteo Italia
3
@glglgl: o ponto é exatamente que, se você não quer causar interrupções no serviço, precisa ir devagar, o tempo em que esse código dorme está lá para permitir que o servidor faça um trabalho realmente útil com o disco.
Matteo Italia
1
+1 para a sleepadição - eu estava tendo problemas com os servidores engasgando com a entrada / saída, apesar de usar ionice -c 3. Ele faz aumentar significativamente o tempo que leva para limpar os arquivos (é claro), mas eu prefiro esperar do que trazer o baixo aplicação ...
Ola Tuvesson
22

Como você está executando o Linux e esta tarefa provavelmente está ligada à E / S, aconselho a dar ao seu planejador a prioridade de E / S inativa usando ionice(1):

ionice -c3 find . -name '*.gif' -delete

Comparando com o seu comando original, acho que isso pode até poupar mais ciclos da CPU, evitando o pipe xargs.


fonte
@Braiam O que você quer dizer? Este não é um find ... -execlugar que faria sentido.
Ah, sim, desculpe. Minha culpa. Você tem certeza de que é eficiente, não é?
Braiam 23/11
1
Bem, a find(1)documentação afirma que sim. :) E deve ser óbvio que deixar- findse remover arquivos é mais eficiente do que bifurcar um rmcomando para isso.
1
Eu tentei várias versões sugeridas em uma pasta com 4 milhões de arquivos em um servidor de produção e essa é a única que não afeta o sistema. ionice -c3reduz o preço para executar apenas quando o IO está ocioso, caso contrário, isso é perfeito. Observe que, como -deletenão é padrão para localização, você pode fazer o mesmo (incluindo o feedback de que funciona) usando este comando: ionice -c 3 find . -name '*.gif' -exec echo {} \; -exec rm {} \;- Lento, mas sem iowaits de processos importantes.
Christopher Lörken
13

Não.

Não existe uma maneira mais rápida, além do formato suave do disco. Os arquivos são fornecidos para rm de uma só vez (até o limite da linha de comando, também pode ser definido como xargs), o que é muito melhor do que chamar rm em cada arquivo. Portanto, não, definitivamente não há caminho mais rápido.

O uso nice(ou reniceem um processo em execução) ajuda apenas parcialmente, porque é para agendar o recurso da CPU , não o disco! E o uso da CPU será muito baixo. Esta é uma fraqueza do linux - se um processo "consome" o disco (ou seja, trabalha muito com ele), toda a máquina fica presa. O kernel modificado para uso em tempo real pode ser uma solução.

O que eu faria no servidor é permitir manualmente que outros processos façam seu trabalho - inclua pausas para manter o servidor "respirando":

find . -name "*.gif" > files
split -l 100 files files.
for F in files.* do
    cat $F | xargs rm
    sleep 5 
done

Isso aguardará 5 segundos após cada 100 arquivos. Levará muito mais tempo, mas seus clientes não devem notar atrasos.

Tomas
fonte
"Os arquivos são entregues para rm de uma só vez (até o limite da linha de comando" - então, quando o shell é solicitado rm *, ele se expande *para a linha com todos os nomes de arquivos e os passa para rm? Isso é incrivelmente estúpido. Por que o shell expandir curingas?
:-D @Joker_vD, você está brincando, como o seu nome sugere? :-)
Tomas
2
@Joker_vD: Compatibilidade com uma decisão Unix de 1970 mais ou menos. O Windows não faz isso. Lá, os programas podem transmitir caracteres curinga para FindNextFile / FindNextFile, para que eles obtenham os resultados um de cada vez.
MSalters 23/11
@ Tomas Não neste caso. Honestamente, vejo imediatamente dois problemas com esse design: primeiro, a linha de comando não é de borracha; segundo, o programa não pode dizer se foi chamado com *ou /*e duvidar dessa decisão do usuário.
1
@Joker_vD Há muitas coisas boas sobre o shell fazendo expansão de curinga. É diferente do Windows, mas não chegue à conclusão de que é incrivelmente estúpido simplesmente porque é diferente do que você está acostumado. Se você quiser saber mais, recomendamos que você pesquise no Google ou poste uma pergunta no site relevante do Stack Exchange. É um enorme descarrilamento para esta área de comentários.
John Kugelman apoia Monica
5

Se o número de arquivos a serem excluídos exceder em muito os arquivos deixados para trás, talvez não seja a abordagem mais eficiente para percorrer a árvore de arquivos a serem excluídos e fazer todas essas atualizações do sistema de arquivos. (É análogo a fazer gerenciamento de memória contado por referência desajeitado, visitar todos os objetos em uma árvore grande para descartar sua referência, em vez de transformar tudo indesejado em lixo em uma etapa e depois varrer o que é acessível para limpar.)

Ou seja, clone as partes da árvore que devem ser mantidas em outro volume. Recrie um sistema de arquivos novo e em branco no volume original. Copie os arquivos retidos de volta aos seus caminhos originais. Isso é vagamente semelhante a copiar a coleta de lixo .

Haverá algum tempo de inatividade, mas poderá ser melhor do que o mau desempenho contínuo e a interrupção do serviço.

Pode ser impraticável em seu sistema e situação, mas é fácil imaginar casos óbvios em que este é o caminho a seguir.

Por exemplo, suponha que você queira excluir todos os arquivos em um sistema de arquivos. Qual seria o sentido de recorrer e excluir um por um? Apenas desmonte e faça um "mkfs" por cima da partição para criar um sistema de arquivos em branco.

Ou suponha que você queira excluir todos os arquivos, exceto os meia dúzia de arquivos importantes? Tire meia dúzia de lá e ... "mkfs" por cima.

Eventualmente, há algum ponto de equilíbrio quando há arquivos suficientes que precisam permanecer, que fica mais barato fazer a exclusão recursiva, levando em conta outros custos, como qualquer tempo de inatividade.

Kaz
fonte
4

Você tentou:

find . -name "*.gif" -exec rm {} +

O sinal + no final fará com que o find inclua mais arquivos para que o comando rm único seja executado. Verifique esta pergunta para mais detalhes.

Bartosz Firyn
fonte
Ele executa muito mais rápido que -print0 | Solução xargs porque o processo rm não é chamado para todos os arquivos, mas para um grande conjunto deles e, portanto, está causando uma carga menor.
@JohnKugelman Você está correto, mas é uma extensão GNU que nem sempre está disponível com o comando find nativo .
CodeGnome
OK, interessante, mas isso é bastante coisa nova (bem como -delete) o que nem sempre tem que estar lá ..
Tomas
No entanto, isso certamente não traz nada melhor em comparação com a solução do OP.
Tomas