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 ;)