Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I have two directories, such that directory B is at least a complete mirror of A. For example:

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

Note that B contains all files present in A, as well as potentially other files.

What I need is to delete all files and directories from B that don't exist in A. In the above example, directory3, file4 and file5 would be deleted, such that after the operation B is a complete mirror of A again with no other files:

B/
    directory1/
        file1
    directory2/
        file2
    file3

For Windows XP.

share|improve this question
    
Do you have powershell? –  soandos Mar 11 '12 at 18:13
    
@soandos: Not that I'm aware of. –  Core Xii Mar 11 '12 at 18:20
    
You could use MS synctoy –  user Mar 12 '12 at 1:37

2 Answers 2

up vote 5 down vote accepted

It's not impossible to do this with a batch script. However, the easy way is to use RoboCopy. Unfortunately, this is only included in Vista and later, but XP users can install this resource kit or grab a standalone copy elsewhere.

robocopy <source> <destination> /mir

From the help (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).

Unfortunately, if you are unable to run outside executables, a batch script will be needed.

Edit:

In the interest of completeness, I've written that batch script. Just make sure to test in a safe place fist, though it seems to work. You may want to remove (or comment, chuck a rem in front) the del and rmdir lines for testing.

@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

Usage is rather simple: save as a .bat file (e.g. purge.bat) and call it like so: purge source destination. If you have spaces in the source or destination paths, you'll have to add quotes, as with any other command line... command.

I'll briefly explain.

Firstly, files are removed. It runs a string replacement, replacing the destination path with the source path, so it can check if the file exists in the source. This allows me to report which files are being removed, and when I get up to removing directories, they are guaranteed to be empty of files. This allows me to avoid the ugliness of rmdir /s, which removes all subdirectories and files, the same as rm -r in Linux. Also, distinguishing between a file and directory just by the path in batch is rather messy.

When I get to removing directories, I build a string in reverse order holding all the directory paths. This is so the deepest level is removed first, because dir /s always provides the deepest levels last. It will, however, likely be in reverse alphabetical order.

Also, thanks for the bash script Eroen, but I can't read bash ;)

share|improve this answer
    
The directory removal part of the batch script doesn't appear to be working. It's not reporting any directories. –  Core Xii Mar 11 '12 at 23:01
    
Whoops.. The dir command in the for loops was failing when the full paths had spaces. I've fixed that now. It's also been tested in XP, and worked. –  Bob Mar 13 '12 at 7:10
    
The directory loop does indeed find directories (tested with echo !curdest!) but the very last loop isn't deleting them. –  Core Xii Mar 13 '12 at 14:12
    
Ah, could you make sure set NL=^ has nothing (including spaces) after the ^, that there are two completely empty lines after it, and maybe save it in Notepad so it uses CR+LF line ending. The NewLine in variables in batch files is a rather dodgy hack. Also, try echo Hello!NL!World just after the second loop and see if it comes out on one line or two. –  Bob Mar 13 '12 at 14:22
    
echo Hello!NL!World comes out as Hello<LF>World<CR+LF>. I tried with both LF and CR+LF line endings for the script, with no apparent effect. The last loop produces nothing; %%a is empty, even outside the if exist. –  Core Xii Mar 13 '12 at 16:41

I'm not much of a scripter for windows batch shell, but the following works in bash. You could then install cygwin and run it. If anyone with more experience with windows scripting wants to port it to batch syntax, go right ahead and steal this answer.

Also, there might be a better way to accomplish this, I did this mostly because it was an interesting challenge, not because I have needed and solved this before.

Setup

$ ls -R
.:
a  b

./a:
i  j  k

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

Solution

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

If you remove the echo command, the rm command will be executed (and delete the files) in stead of just printed. This way is just better for verification.

In my simple "wet" test it worked nicely with filenames containing spaces.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.