Exclui arquivos que não existem em outro diretório

4

Eu tenho dois diretórios, de modo que o diretório B seja pelo menos um espelho completo de A . Por exemplo:

A/
    directory1/
        file1
    directory2/
        file2
    file3
B/
    directory1/
        file1
    directory2/
        file2
    directory3/
        file4
    file3
    file5

Observe que B contém todos os arquivos presentes em A , além de outros arquivos.

O que eu preciso é excluir todos os arquivos e diretórios de B que não existem em A . No exemplo acima, directory3 , file4 e file5 seriam excluídos, de modo que, após a operação B seja um espelho completo de A novamente sem outros arquivos:

B/
    directory1/
        file1
    directory2/
        file2
    file3

Para o Windows XP.

    
por Core Xii 11.03.2012 / 18:42

2 respostas

5

Não é impossível fazer isso com um script em lote. No entanto, o caminho mais fácil é usar RoboCopy . Infelizmente, isso é incluído apenas no Vista e em versões posteriores, mas os usuários do XP podem instalar o este kit de recursos ou pegue uma cópia independente em outro lugar.

robocopy <source> <destination> /mir

Da ajuda ( robocopy /? ):

/E :: copy subdirectories, including Empty ones.
/PURGE :: delete dest files/dirs that no longer exist in source.
/MIR :: MIRror a directory tree (equivalent to /E plus /PURGE).

Infelizmente, se você não conseguir executar executáveis externos, será necessário um script em lote.

Editar:

No interesse da integralidade, eu escrevi esse script em lote. Apenas certifique-se de testar em um punho de lugar seguro, embora pareça funcionar. Você pode querer remover (ou comentar, coloque um rem na frente) as linhas del e rmdir para testes.

@echo off

setlocal EnableDelayedExpansion

set dirsrc=%~f1
set dirdest=%~f2

echo Source: "%dirsrc%"
echo Destination: "%dirdest%"

echo.
echo.
echo Cleaning files...

for /f "tokens=*" %%a in ('dir /s /b /a:-d "%dirdest%"') do (
    set curdest=%%a
    set cursrc=!curdest:%dirdest%=%dirsrc%!

    if not exist !cursrc! (
        echo Deleting "!curdest!"
        del "!curdest!"
    )
)

echo.
echo.
echo Cleaning directories...

set NLM=^


set NL=^^^%NLM%%NLM%^%NLM%%NLM%
rem Two lines left empty for NewLine to work

for /f "tokens=*" %%a in ('dir /s /b /a:d "%dirdest%"') do (
    set curdest=%%a
    set cursrc=!curdest:%dirdest%=%dirsrc%!

    if not exist !cursrc! (
        set rmlist=!curdest!?!rmlist!
    )
)

set rmlist=!rmlist:?=%NL%!

for /f "tokens=*" %%a in ("!rmlist!") do (
    if exist %%a (
        echo Removing directory "%%a"
        rmdir "%%a"
    )
)

endlocal

O uso é bastante simples: salve como um arquivo .bat (por exemplo, purge.bat) e chame da seguinte forma: purge source destination . Se você tiver espaços nos caminhos de origem ou de destino, terá que adicionar aspas, como em qualquer outro comando de linha de comando.

Vou explicar brevemente.

Primeiramente, os arquivos são removidos. Ele executa uma substituição de string, substituindo o caminho de destino pelo caminho de origem, para que ele possa verificar se o arquivo existe na origem. Isso me permite informar quais arquivos estão sendo removidos e, quando eu for remover os diretórios, eles estarão vazios. Isso me permite evitar a fealdade de rmdir /s , que remove todos os subdiretórios e arquivos, o mesmo que rm -r no Linux. Além disso, distinguir entre um arquivo e um diretório apenas pelo caminho em lote é bastante confuso.

Quando eu removo os diretórios, eu construo uma string em ordem reversa, mantendo todos os caminhos do diretório. É assim que o nível mais profundo é removido primeiro, porque dir /s sempre fornece os níveis mais profundos por último. Será, no entanto, provavelmente em ordem alfabética inversa.

Além disso, obrigado pelo script basico Eroen, mas não consigo ler o bash;)

    
por 11.03.2012 / 19:26
0

Eu não sou muito de um scripter para shell de lote do windows, mas o seguinte funciona no bash. Você poderia, então, instalar o cygwin e executá-lo. Se alguém com mais experiência com o script do Windows quiser portá-lo para a sintaxe em lote, vá em frente e roube essa resposta.

Além disso, pode haver uma maneira melhor de conseguir isso, eu fiz isso principalmente porque foi um desafio interessante, não porque eu precisei e resolvi isso antes.

Configuração

$ ls -R
.:
a  b

./a:
i  j  k

./b:
i  j  k  l
$ cd b/

Solução

$ for i in *
> do
> [[ -e "../a/$i" ]] || echo rm -r "${i}"
> done
rm l

Se você remover o comando echo , o comando rm será executado (e excluirá os arquivos) em vez de apenas impresso. Desta forma, é melhor apenas para verificação.

No meu teste "wet" simples, funcionou bem com nomes de arquivos contendo espaços.

    
por 11.03.2012 / 19:10