-->

terça-feira, 26 de agosto de 2008

Como apagar apenas pastas vazias recursivamente.

Por default, a cada backup o Cobian Backup recria a estrutura de pastas da origem no destino mesmo que não haja nada a ser copiado nelas. Se o backup estiver configurado para incremental, mesmo que nenhum arquivo tenha sido modificado e precise ser backupeado (palavrinha esquisita), o Cobian cria toda a estrutura de pastas vazias no destino. Eu ainda não consegui enxergar qual a utilidade disso, mas encontrei problemas que podem ficar enormes dependendo do cenário:

  • Fica desnecessariamente complicado achar os arquivos que mudaram entre backups;
  • Fica desnecessariamente complicado mesclar o backup full com os backups incrementais, caso ocorra um acidente e o backup precise ser recuperado;
  • Se houverem muitas pastas, todas as operações no backup podem ficar absurdamente lentas, sem nenhuma vantagem prática;

Eu pude sentir o custo disso quando o HDD de um cliente pifou e fui recuperar os arquivos dele no backup. Esse cliente tem uma estrutura de pastas colossal para sua atividade: cerca de 40mil pastas. Como cada backup incremental recriava a estrutura, por menos arquivos que realmente fossem backupeados, o HDD de backup dele estava com centenas de milhares de pastas cheias de nada.

O problema é que só para pedir as propriedades de cada backup e constatar que não havia nada além de pastas vazias, leva um minuto. Para apagar esse backup vazio o Explorer levava outros vários minutos. Quando não havia nenhum arquivo era até fácil. O problema é quando eu constatava que o backup tinha vários arquivos e ou optava por copiar cada um deles para o lugar certo (muito sujeito a erro) ou mandava o Explorer copiar aquelas dezenas de milhares de pastas vazias sobre as pastas cheias. Essa operação não oferece risco de perda de dados mas é angustiantemente demorada.

Nota: O Cobian tem uma configuração para deletar as pastas vazias e não deixar que isso ocorra (Tools -> Options -> Engine), mas quando eu percebi que esse cliente estava configurado para criar as pastas, já era tarde.

Antes desse cliente perder o HDD eu já havia percebido esse problema em outro cliente, por isso já pesquisara uma solução para apagar as pastas vazias (ou assim eu pensara): Remove Empty Directories (RED). O Problema do RED é que, além de requerer o .NET framework 2.0 instalado (um grande ponto negativo na minha avaliação de qualquer software de manutenção) seu modo de operação só funciona bem para uma quantidade "não-obscena" de pastas. Eu testei em casa com umas poucas centenas de pastas vazias e tudo correu bem, mas quando tentei usar com esse cliente foi uma enorme decepção porque o programa levou dezenas de minutos somente para mostrar o (desnecessário) gráfico com as pastas que seriam apagadas, para só depois começar a apagar. E mesmo assim deu erro minutos depois e o processo foi abortado! Mesmo que o RED tivesse funcionado ia levar muito mais tempo que o necessário porque ele atualiza o gráfico de pastas em tempo real durante o apagamento.

Como eu não podia arriscar os arquivos do único backup atualizado do cliente, também não podia arriscar "testar" outro programa, então tive que fazer todo o processo de restauração com o "overhead" das pastas vazias.

Eu estive pesquisando outras opções e aparentemente a que melhor atende minhas necessidades está neste post de Raymond Chen. Raymond apresenta este comando batch como solução:

for /f "usebackq" %%d in (`"dir /ad/b/s | sort /R"`) do rd "%%d"

Mas como é corretamente observado em um dos comentários, esse comando falha se a pasta tiver espaços em seu caminho/nome. Soluções foram dadas mais adiante por outros usuários:

for /f "usebackq delims=" %%d in (`"dir /ad/b/s | sort /R"`) do rd "%%d"

ou, numa versão menor:

for /f "tokens=*" %%d in ('dir /ad/b/s ^| sort /R') do rd "%%d"

Grave esta linha em um arquivo batch e execute na raiz da pasta a partir da qual quer apagar as pastas vazias.

A segurança deste scritpt está no fato de que usa o comando RD do Windows para apagar as pastas. E esse comando não apaga pastas que não estejam vazias, a menos que receba o parâmetro "/S".

Eu testei hoje com um cliente cujo backup estava assim:

19026 arquivos - 12659 pastas

E em dois minutos o script deixou o backup assim:

19026 arquivos - 1734 pastas

Como se pode ver, mais de 10 mil pastas desnecessárias foram excluídas, mas nenhum arquivo foi perdido.

AVISO 1: Não rode esses scripts em uma máquina remota acessando por um caminho UNC. O script vai rodar localmente e apagar as pastas vazias no lugar errado (geralmente sob c:\windows). Se você acha que corre o risco de fazer isso, coloque um comando PAUSE no início do script para ver o aviso do Windows e poder abortar a operação.

AVISO 2: Cuidado com algumas outras sugestões dadas nos comentários daquele post. Como é bem observado por outros usuários, pelo menos duas pessoas propuseram soluções "muito mais simples", que apagam todas as pastas vazias... e todos os seus arquivos!

AVISO 3: Pastas vazias não são necessariamente inúteis e é altamente recomendável que você não se arrisque a apagar pastas vazias "só porque elas existem". Rodar um programa destes em um backup ou numa pasta de documentos é algo que oferece riscos desprezíveis, mas cuidado com o problema que você vai arrumar para si mesmo se rodar isso em C:

9 comentários:

  1. Du K !!!!! valew irmaoo!!!

    ResponderExcluir
  2. Muito bom, obrigado pela informação, exatamente o que precisava.

    abraço

    ResponderExcluir
  3. Existe outro modo de fazer isso, possibilitando inclusive a utilização em maquina
    remota e vizualização do log de pastas. Para fins de compatibilidade desenvolvi
    esse código em Windows7.

    ::----------------------------------------------------
    @ECHO OFF

    SET "ROOT=%~dp0"

    :START

    ::----------------------------------------------------
    SET "LOG=%~nn0.log"

    CD /D "%~dp0"

    ECHO.

    ECHO Pasta atual: %ROOT%

    SET /P "ROOT=Arraste uma pasta raiz e solte aqui ou tecle ENTER para a atual: "

    PUSHD "%ROOT%"

    DIR /B /A:d /S>"%~dp0%LOG%"

    POPD

    FOR /F "EOL=. TOKENS=* DELIMS=" %%A IN (%LOG%) DO (ECHO."%%A"&RMDIR "%%A")

    ECHO.

    GOTO :START

    ResponderExcluir
  4. 100%!! Simples e funcional! valeu a dica!

    ResponderExcluir
  5. Quando eu escrevi este post, dez anos atrás, eu achava que 17 mil pastas eram muito. Agora estou há dias levando uma surra de uma estrutura com dois milhões de pastas. Deixei o batch rodando no servidor por dois dias e ele sequer começou a apagar as pastas.

    Cancelei e dividi experimentalmente o processamento em lotes menores de cerca de 240 mil pastas cada. Abri três processos simultâneos. Cada script fica mais de uma hora sem nem começar a apagar mas depois de cerca de 1h40 os três processos estão concluídos (720 mil pastas processadas). Eu não criei mais processos porque os três juntos estavam ocupando 100% da capacidade do processador.

    Em uma outra experiência eu tentei processar com um processo só 480 mil pastas. Após duas horas nem havia começado a apagar, por isso cancelei. O que sugere que em algum ponto acima de 240 mil pastas esse script começa a desenvolver um problema ou é realmente melhor paralelizar.

    Dividi o lote anterior em dois processos e o tempo de execução foi também de 1h40 apesar do consumo reduzido de CPU, o que sugere que usar 100% da CPU nessa tarefa é uma boa estratégia.

    ResponderExcluir
  6. Experimento realizado em um Athlon 5350 APU e Windows 7 32 bits com 2.45GB utilizáveis de RAM

    ResponderExcluir
  7. Novo teste. Dividi um grupo de 280 mil pastas em dois processos. Levou apenas 40 minutos. Levaria mais de uma hora e meia em um processo apenas. Está ficando claro que o processo desacelera seguindo uma curva não linear com o aumento do número de pastas.

    ResponderExcluir
  8. Excelente dica. Como faço para chamar este comando em um arquivo bat para apagar pastas vazias em outro disco?

    ResponderExcluir
    Respostas
    1. Pra isso será necessario altera o diretorio que o Prompt ira executar o comando.

      adicione a seguinte linha, antes do comando original do post:

      CD /d "c:\seuDiretorio"

      Excluir

Siga as regras do blog ou seu comentário será ignorado.