-->

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:

4 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

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