By Kibbee


2008-09-09 01:05:28 8 Comments

I am looking for a way to delete all files older than 7 days in a batch file. I've searched around the web, and found some examples with hundreds of lines of code, and others that required installing extra command line utilities to accomplish the task.

Similar things can be done in BASH in just a couple lines of code. It seems that something at least remotely easy could be done for batch files in Windows. I'm looking for a solution that works in a standard Windows command prompt, without any extra utilities. Please no PowerShell or Cygwin either.

23 comments

@GBGOLC 2018-03-16 13:33:56

Gosh, a lot of answers already. A simple and convenient route I found was to execute ROBOCOP.EXE twice in sequential order from a single Windows command line instruction using the & parameter.

ROBOCOPY.EXE SOURCE-DIR TARGET-DIR *.* /MOV /MINAGE:30 & ROBOCOPY.EXE SOURCE-DIR TARGET-DIR *.* /MOV /MINAGE:30 /PURGE

In this example it works by picking all files (.) that are older than 30 days old and moving them to the target folder. The second command does the same again with the addition of the PURGE command which means remove files in the target folder that don’t exist in the source folder. So essentially, the first command MOVES files and the second DELETES because they no longer exist in the source folder when the second command is invoked.

Consult ROBOCOPY's documentation and use the /L switch when testing.

@Jay 2009-08-24 14:55:55

Ok was bored a bit and came up with this, which contains my version of a poor man's Linux epoch replacement limited for daily usage (no time retention):

7daysclean.cmd

@echo off
setlocal ENABLEDELAYEDEXPANSION
set day=86400
set /a year=day*365
set /a strip=day*7
set dSource=C:\temp

call :epoch %date%
set /a slice=epoch-strip

for /f "delims=" %%f in ('dir /a-d-h-s /b /s %dSource%') do (
    call :epoch %%~tf
    if !epoch! LEQ %slice% (echo DELETE %%f ^(%%~tf^)) ELSE echo keep %%f ^(%%~tf^)
)
exit /b 0

rem Args[1]: Year-Month-Day
:epoch
    setlocal ENABLEDELAYEDEXPANSION
    for /f "tokens=1,2,3 delims=-" %%d in ('echo %1') do set Years=%%d& set Months=%%e& set Days=%%f
    if "!Months:~0,1!"=="0" set Months=!Months:~1,1!
    if "!Days:~0,1!"=="0" set Days=!Days:~1,1!
    set /a Days=Days*day
    set /a _months=0
    set i=1&& for %%m in (31 28 31 30 31 30 31 31 30 31 30 31) do if !i! LSS !Months! (set /a _months=!_months! + %%m*day&& set /a i+=1)
    set /a Months=!_months!
    set /a Years=(Years-1970)*year
    set /a Epoch=Years+Months+Days
    endlocal& set Epoch=%Epoch%
    exit /b 0

USAGE

set /a strip=day*7 : Change 7 for the number of days to keep.

set dSource=C:\temp : This is the starting directory to check for files.

NOTES

This is non-destructive code, it will display what would have happened.

Change :

if !epoch! LEQ %slice% (echo DELETE %%f ^(%%~tf^)) ELSE echo keep %%f ^(%%~tf^)

to something like :

if !epoch! LEQ %slice% del /f %%f

so files actually get deleted

February: is hard-coded to 28 days. Bissextile years is a hell to add, really. if someone has an idea that would not add 10 lines of code, go ahead and post so I add it to my code.

epoch: I did not take time into consideration, as the need is to delete files older than a certain date, taking hours/minutes would have deleted files from a day that was meant for keeping.

LIMITATION

epoch takes for granted your short date format is YYYY-MM-DD. It would need to be adapted for other settings or a run-time evaluation (read sShortTime, user-bound configuration, configure proper field order in a filter and use the filter to extract the correct data from the argument).

Did I mention I hate this editor's auto-formating? it removes the blank lines and the copy-paste is a hell.

I hope this helps.

@Haugholt 2014-09-10 08:09:14

+1 for non-destructive code, I'll try and keep that in mind when I provide code examples myself.

@Flonk 2017-06-19 14:02:29

Way late and totally unrelated, but 7daysclean.cmd sounds like an awesome name for a Synth-Punk band.

@efdummy 2017-07-14 23:47:10

My script to delete files older than a specific year :

@REM _______ GENERATE A CMD TO DELETE FILES OLDER THAN A GIVEN YEAR
@REM _______ (given in _olderthanyear variable)
@REM _______ (you must LOCALIZE the script depending on the dir cmd console output)
@REM _______ (we assume here the following line's format "11/06/2017  15:04            58 389 SpeechToText.zip")

@set _targetdir=c:\temp
@set _olderthanyear=2017

@set _outfile1="%temp%\deleteoldfiles.1.tmp.txt"
@set _outfile2="%temp%\deleteoldfiles.2.tmp.txt"

  @if not exist "%_targetdir%" (call :process_error 1 DIR_NOT_FOUND "%_targetdir%") & (goto :end)

:main
  @dir /a-d-h-s /s /b %_targetdir%\*>%_outfile1%
  @for /F "tokens=*" %%F in ('type %_outfile1%') do @call :process_file_path "%%F" %_outfile2%
  @goto :end

:end
  @rem ___ cleanup and exit
  @if exist %_outfile1% del %_outfile1%
  @if exist %_outfile2% del %_outfile2%
  @goto :eof

:process_file_path %1 %2
  @rem ___ get date info of the %1 file path
  @dir %1 | find "/" | find ":" > %2
  @for /F "tokens=*" %%L in ('type %2') do @call :process_line "%%L" %1
  @goto :eof

:process_line %1 %2
  @rem ___ generate a del command for each file older than %_olderthanyear%
  @set _var=%1
  @rem  LOCALIZE HERE (char-offset,string-length)
  @set _fileyear=%_var:~0,4%
  @set _fileyear=%_var:~7,4%
  @set _filepath=%2
  @if %_fileyear% LSS %_olderthanyear% echo @REM %_fileyear%
  @if %_fileyear% LSS %_olderthanyear% echo @del %_filepath%
  @goto :eof

:process_error %1 %2
  @echo RC=%1 MSG=%2 %3
  @goto :eof

@aku 2008-09-09 01:18:27

Enjoy:

forfiles -p "C:\what\ever" -s -m *.* -d <number of days> -c "cmd /c del @path"

See forfile documentation for more details.

For more goodies, refer to An A-Z Index of the Windows XP command line.

If you don't have forfiles installed on your machine, copy it from any Windows Server 2003 to your Windows XP machine at %WinDir%\system32\. This is possible since the EXE is fully compatible between Windows Server 2003 and Windows XP.

Later versions of Windows and Windows Server have it installed by default.

For Windows 7:

The syntax has changed a little. Therefore the updated command is:

forfiles /p "C:\what\ever" /s /m *.* /D -<number of days> /C "cmd /c del @path"

@Blorgbeard 2008-09-09 01:19:59

I don't have forfiles on my XP (sp3) machine - where'd that come from?

@Russell Steen 2009-09-16 14:55:41

Should be del @FILE, case matters Also I suggest using /c echo @FILE for testing

@gregmac 2010-03-18 16:27:01

@Russell: @PATH is the full path, including name. @FILE is only the name, so if you're dealing with subfolders, it won't work.

@gregmac 2010-03-18 16:28:25

Note that if you want files OLDER than 10 days, you need to specify -d "-10". -ve means "older than", +ve means "newer than". You can also specify DDMMYY or -DDMMYY format as the parameter to -d.

@Chris Vesper 2010-08-18 18:40:09

When I tested it under Windows 7, @PATH was just the path, and @FILE was just the file name. I used @PATH\@FILE for the del command.

@Chris J 2010-10-14 14:35:23

Well you learn something new every day... never come across forfiles before :-)

@Dragos Durlut 2011-03-19 12:45:45

Also, if you wish to get files from another drive: for example all rar files older than 30 days C:\Windows\System32>forfiles -p "F:\\" -m *.rar -d -30 -c "cmd /c echo These wil l be deleted @fname" These will be deleted "File1" These will be deleted "File2"

@jman 2011-04-18 08:42:47

I used this syntax on Win Server 2008: forfiles /P "C:\Mysql_backup" /S /M *.sql /D -30 /C "cmd /c del @PATH"

@TrekCzar 2011-12-14 17:37:10

also adding ECHO "Y" helped with the Are you sure ? messages like this: "cmd /c echo "Y" | del @PATH" at the end

@Dave Markle 2012-01-17 19:16:36

Amazing, but too late for me as I've made the conscious decision to never write a batch file again, and force myself to go exclusively with PowerShell.

@Finglas 2012-07-03 15:01:29

Thanks. I should add that if the days range is less than whatever you are trying to delete, the error will state no matching files were found. This is correct.

@Gaspa79 2012-07-19 15:52:57

remember that the number of days should be negative, say for example /d -10 will delete everything before 10 days. A positive value makes almost no sense.

@mavrosxristoforos 2013-09-20 08:31:32

Warning: If you are just trying to delete files you copied N days ago, this is not what you should use. I just copied some files and run 'forfiles /P "path" /S /C "cmd /c echo @file" /D -31' on them, and the files that were modified more than 31 days ago do appear.

@TimS 2013-10-18 10:50:24

This answer works when "older than 7 days" is defined as "modified more than 7 days ago" rather than "created more than 7 days ago". How can the latter be achieved?

@Gennady Vanin Геннадий Ванин 2013-10-25 01:37:33

Unfortunately, on file share destination it gives the error: "ERROR: UNC paths paths (\\machine\share) are not supported"

@Tastybrownies 2014-07-13 14:47:44

This doesn't natively work on XP Professional.

@Matthew Lock 2014-10-08 02:16:04

I've adapted this command to use MOVE instead of DEL in order to archive the old files into another folder computerhope.com/movehlp.htm

@storm_m2138 2014-12-08 18:16:09

To skip over Locked files / files in use by another process (instead of hanging when you encounter them) -- use remove.exe from virtualobjectives.com.au/utilitiesprogs/remove.htm

@AnT 2015-11-21 17:26:00

Um... Why does this answer post the "updated" version of that command line, yet still uses old syntax for about half of the command-line switches in that "updated" version? The official syntax for command-line switches in Windows forfiles begins with /, not with -. Also, what is the *.* searchmask doing there? This will restrict the scope to files with . in their names. Why? The OP never made such request. The default searchmask for forfiles is * and it appears that the OP needs exactly that. Meaning that there's no need for /M parameter at all.

@Stephan 2015-12-30 09:57:04

Note that the non-Windows 7 syntax requires a dash (-) before the <number of days> to work.

@Brian B 2016-04-05 17:07:53

I did not see anyone mention that you should use if @isdir==FALSE to process only the files, and not the folders. Otherwise you may accidentally delete all the files in a folder. Example: forfiles -p "C:\what\ever" -s -m . /D -<number of days> /C "cmd /c if @isdir==FALSE del @path"

@user15964 2016-07-18 02:13:42

@gregmac Hi, gregmac. Are you sure about this? I test it and found @file also works for files in subfolders

@Trefex 2016-10-11 09:25:22

on Windows 7, the path directory should not be double quoted. See stackoverflow.com/questions/1039221/…

@Steve Chambers 2016-10-14 11:08:38

Another way to avoid "Are you sure" messages is del /F /Q @path - see stackoverflow.com/questions/7160342#7160366.

@wruckie 2017-08-07 21:30:11

@TrekOnTV2017 Thank you for the echo "Y", I have 193k files to delete and answering each prompt was a no go!

@Ori Wiesel 2017-11-28 14:13:36

is there any way to apply it on sub directories?

@Snickbrack 2017-05-19 08:49:54

This one did it for me. It works with a date and you can substract the wanted amount in years to go back in time:

@echo off

set m=%date:~-7,2%
set /A m
set dateYear=%date:~-4,4%
set /A dateYear -= 2
set DATE_DIR=%date:~-10,2%.%m%.%dateYear% 

forfiles /p "C:\your\path\here\" /s /m *.* /d -%DATE_DIR% /c "cmd /c del @path /F"

pause

the /F in the cmd /c del @path /F forces the specific file to be deleted in some the cases the file can be read-only.

the dateYear is the year Variable and there you can change the substract to your own needs

@Shawn Pauliszyn 2016-08-15 15:29:17

ROBOCOPY works great for me. Originally suggested my Iman. But instead of moving the files/folders to a temporary directory then deleting the contents of the temporary folder, move the files to the trash!!!

This is is a few lines of my backup batch file for example:

SET FilesToClean1=C:\Users\pauls12\Temp
SET FilesToClean2=C:\Users\pauls12\Desktop\1616 - Champlain\Engineering\CAD\Backups

SET RecycleBin=C:\$Recycle.Bin\S-1-5-21-1480896384-1411656790-2242726676-748474

robocopy "%FilesToClean1%" "%RecycleBin%" /mov /MINLAD:15 /XA:SH /NC /NDL /NJH /NS /NP /NJS
robocopy "%FilesToClean2%" "%RecycleBin%" /mov /MINLAD:30 /XA:SH /NC /NDL /NJH /NS /NP /NJS

It cleans anything older than 15 days out of my 'Temp' folder and 30 days for anything in my AutoCAD backup folder. I use variables because the line can get quite long and I can reuse them for other locations. You just need to find the dos path to your recycle bin associated with your login.

This is on a work computer for me and it works. I understand that some of you may have more restrictive rights but give it a try anyway;) Search Google for explanations on the ROBOCOPY parameters.

Cheers!

@Jonathan Leffler 2016-08-15 16:07:31

The existing second-most-upvoted answer also espouses RoboCopy. Granted, you use somewhat different options, but it is close to the existing answer.

@Shawn Pauliszyn 2016-09-01 18:07:23

This has post has a new content--moving files to the recycle bin. This has not yet been been proposed with Robocopy and it fully answers the original post using a built-in single-line command. Works better than combining 'DEL' and 'RMDIR' commands because the files can be restored from the recycle bin. Hey I'm still new here--give me a break;)

@Mofi 2015-02-08 15:13:11

There are very often relative date/time related questions to solve with batch file. But command line interpreter cmd.exe has no function for date/time calculations. Lots of good working solutions using additional console applications or scripts have been posted already here, on other pages of Stack Overflow and on other websites.

Common for operations based on date/time is the requirement to convert a date/time string to seconds since a determined day. Very common is 1970-01-01 00:00:00 UTC. But any later day could be also used depending on the date range required to support for a specific task.

Jay posted 7daysclean.cmd containing a fast "date to seconds" solution for command line interpreter cmd.exe. But it does not take leap years correct into account. J.R. posted an add-on for taking leap day in current year into account, but ignoring the other leap years since base year, i.e. since 1970.

I use since 20 years static tables (arrays) created once with a small C function for quickly getting the number of days including leap days from 1970-01-01 in date/time conversion functions in my applications written in C/C++.

This very fast table method can be used also in batch code using FOR command. So I decided to code the batch subroutine GetSeconds which calculates the number of seconds since 1970-01-01 00:00:00 UTC for a date/time string passed to this routine.

Note: Leap seconds are not taken into account as the Windows file systems also do not support leap seconds.

First, the tables:

  1. Days since 1970-01-01 00:00:00 UTC for each year including leap days.

    1970 - 1979:     0   365   730  1096  1461  1826  2191  2557  2922  3287
    1980 - 1989:  3652  4018  4383  4748  5113  5479  5844  6209  6574  6940
    1990 - 1999:  7305  7670  8035  8401  8766  9131  9496  9862 10227 10592
    2000 - 2009: 10957 11323 11688 12053 12418 12784 13149 13514 13879 14245
    2010 - 2019: 14610 14975 15340 15706 16071 16436 16801 17167 17532 17897
    2020 - 2029: 18262 18628 18993 19358 19723 20089 20454 20819 21184 21550
    2030 - 2039: 21915 22280 22645 23011 23376 23741 24106 24472 24837 25202
    2040 - 2049: 25567 25933 26298 26663 27028 27394 27759 28124 28489 28855
    2050 - 2059: 29220 29585 29950 30316 30681 31046 31411 31777 32142 32507
    2060 - 2069: 32872 33238 33603 33968 34333 34699 35064 35429 35794 36160
    2070 - 2079: 36525 36890 37255 37621 37986 38351 38716 39082 39447 39812
    2080 - 2089: 40177 40543 40908 41273 41638 42004 42369 42734 43099 43465
    2090 - 2099: 43830 44195 44560 44926 45291 45656 46021 46387 46752 47117
    2100 - 2106: 47482 47847 48212 48577 48942 49308 49673
    

    Calculating the seconds for year 2039 to 2106 with epoch beginning 1970-01-01 is only possible with using an unsigned 32-bit variable, i.e. unsigned long (or unsigned int) in C/C++.

    But cmd.exe use for mathematical expressions a signed 32-bit variable. Therefore the maximum value is 2147483647 (0x7FFFFFFF) which is 2038-01-19 03:14:07.

  2. Leap year information (No/Yes) for the years 1970 to 2106.

    1970 - 1989: N N Y N N N Y N N N Y N N N Y N N N Y N
    1990 - 2009: N N Y N N N Y N N N Y N N N Y N N N Y N
    2010 - 2029: N N Y N N N Y N N N Y N N N Y N N N Y N
    2030 - 2049: N N Y N N N Y N N N Y N N N Y N N N Y N
    2050 - 2069: N N Y N N N Y N N N Y N N N Y N N N Y N
    2070 - 2089: N N Y N N N Y N N N Y N N N Y N N N Y N
    2090 - 2106: N N Y N N N Y N N N N N N N Y N N
                                     ^ year 2100
    
  3. Number of days to first day of each month in current year.

                       Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
    Year with 365 days:  0  31  59  90 120 151 181 212 243 273 304 334
    Year with 366 days:  0  31  60  91 121 152 182 213 244 274 305 335
    

Converting a date to number of seconds since 1970-01-01 is quite easy using those tables.

Attention please!

The format of date and time strings depends on Windows region and language settings. The delimiters and the order of tokens assigned to the environment variables Day, Month and Year in first FOR loop of GetSeconds must be adapted to local date/time format if necessary.

It is necessary to adapt the date string of the environment variable if date format in environment variable DATE is different to date format used by command FOR on %%~tF.

For example when %DATE% expands to Sun 02/08/2015 while %%~tF expands to 02/08/2015 07:38 PM the code below can be used with modifying line 4 to:

call :GetSeconds "%DATE:~4% %TIME%"

This results in passing to subroutine just 02/08/2015 - the date string without the 3 letters of weekday abbreviation and the separating space character.

Alternatively following could be used to pass current date in correct format:

call :GetSeconds "%DATE:~-10% %TIME%"

Now the last 10 characters from date string are passed to function GetSeconds and therefore it does not matter if date string of environment variable DATE is with or without weekday as long as day and month are always with 2 digits in expected order, i.e. in format dd/mm/yyyy or dd.mm.yyyy.

Here is the batch code with explaining comments which just outputs which file to delete and which file to keep in C:\Temp folder tree, see code of first FOR loop.

@echo off
setlocal EnableExtensions EnableDelayedExpansion
rem Get seconds since 1970-01-01 for current date and time.
call :GetSeconds "%DATE% %TIME%"
rem Subtract seconds for 7 days from seconds value.
set /A "LastWeek=Seconds-7*86400"

rem For each file in each subdirectory of C:\Temp get last modification date
rem (without seconds -> append second 0) and determine the number of seconds
rem since 1970-01-01 for this date/time. The file can be deleted if seconds
rem value is lower than the value calculated above.

for /F "delims=" %%F in ('dir /A-D-H-S /B /S "C:\Temp"') do (
    call :GetSeconds "%%~tF:0"
    rem if !Seconds! LSS %LastWeek% del /F "%%~fF"
    if !Seconds! LEQ %LastWeek% (
        echo Delete "%%~fF"
    ) else (
        echo Keep   "%%~fF"
    )
)
endlocal
goto :EOF


rem No validation is made for best performance. So make sure that date
rem and hour in string is in a format supported by the code below like
rem MM/DD/YYYY hh:mm:ss or M/D/YYYY h:m:s for English US date/time.

:GetSeconds

rem If there is " AM" or " PM" in time string because of using 12 hour
rem time format, remove those 2 strings and in case of " PM" remember
rem that 12 hours must be added to the hour depending on hour value.

set "DateTime=%~1"
set "Add12Hours=0"
if "%DateTime: AM=%" NEQ "%DateTime%" (
    set "DateTime=%DateTime: AM=%"
) else if "%DateTime: PM=%" NEQ "%DateTime%" (
    set "DateTime=%DateTime: PM=%"
    set "Add12Hours=1"
)

rem Get year, month, day, hour, minute and second from first parameter.

for /F "tokens=1-6 delims=,-./: " %%A in ("%DateTime%") do (
    rem For English US date MM/DD/YYYY or M/D/YYYY
    set "Day=%%B" & set "Month=%%A" & set "Year=%%C"
    rem For German date DD.MM.YYYY or English UK date DD/MM/YYYY
    rem set "Day=%%A" & set "Month=%%B" & set "Year=%%C"
    set "Hour=%%D" & set "Minute=%%E" & set "Second=%%F"
)
rem echo Date/time is: %Year%-%Month%-%Day% %Hour%:%Minute%:%Second%

rem Remove leading zeros from the date/time values or calculation could be wrong.
if "%Month:~0,1%"  EQU "0" ( if "%Month:~1%"  NEQ "" set "Month=%Month:~1%"   )
if "%Day:~0,1%"    EQU "0" ( if "%Day:~1%"    NEQ "" set "Day=%Day:~1%"       )
if "%Hour:~0,1%"   EQU "0" ( if "%Hour:~1%"   NEQ "" set "Hour=%Hour:~1%"     )
if "%Minute:~0,1%" EQU "0" ( if "%Minute:~1%" NEQ "" set "Minute=%Minute:~1%" )
if "%Second:~0,1%" EQU "0" ( if "%Second:~1%" NEQ "" set "Second=%Second:~1%" )

rem Add 12 hours for time range 01:00:00 PM to 11:59:59 PM,
rem but keep the hour as is for 12:00:00 PM to 12:59:59 PM.
if "%Add12Hours%" == "1" (
    if %Hour% LSS 12 set /A Hour+=12
)
set "DateTime="
set "Add12Hours="

rem Must use 2 arrays as more than 31 tokens are not supported
rem by command line interpreter cmd.exe respectively command FOR.
set /A "Index1=Year-1979"
set /A "Index2=Index1-30"

if %Index1% LEQ 30 (
    rem Get number of days to year for the years 1980 to 2009.
    for /F "tokens=%Index1% delims= " %%Y in ("3652 4018 4383 4748 5113 5479 5844 6209 6574 6940 7305 7670 8035 8401 8766 9131 9496 9862 10227 10592 10957 11323 11688 12053 12418 12784 13149 13514 13879 14245") do set "Days=%%Y"
    for /F "tokens=%Index1% delims= " %%L in ("Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N") do set "LeapYear=%%L"
) else (
    rem Get number of days to year for the years 2010 to 2038.
    for /F "tokens=%Index2% delims= " %%Y in ("14610 14975 15340 15706 16071 16436 16801 17167 17532 17897 18262 18628 18993 19358 19723 20089 20454 20819 21184 21550 21915 22280 22645 23011 23376 23741 24106 24472 24837") do set "Days=%%Y"
    for /F "tokens=%Index2% delims= " %%L in ("N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N") do set "LeapYear=%%L"
)

rem Add the days to month in year.
if "%LeapYear%" == "N" (
    for /F "tokens=%Month% delims= " %%M in ("0 31 59 90 120 151 181 212 243 273 304 334") do set /A "Days+=%%M"
) else (
    for /F "tokens=%Month% delims= " %%M in ("0 31 60 91 121 152 182 213 244 274 305 335") do set /A "Days+=%%M"
)

rem Add the complete days in month of year.
set /A "Days+=Day-1"

rem Calculate the seconds which is easy now.
set /A "Seconds=Days*86400+Hour*3600+Minute*60+Second"

rem Exit this subroutine
goto :EOF

For optimal performance it would be best to remove all comments, i.e. all lines starting with rem after 0-4 leading spaces.

And the arrays can be made also smaller, i.e. decreasing the time range from 1980-01-01 00:00:00 to 2038-01-19 03:14:07 as currently supported by the batch code above for example to 2015-01-01 to 2019-12-31 as the code below uses which really deletes files older than 7 days in C:\Temp folder tree.

Further the batch code below is optimized for 24 hours time format.

@echo off
setlocal EnableDelayedExpansion
call :GetSeconds "%DATE:~-10% %TIME%"
set /A "LastWeek=Seconds-7*86400"

for /F "delims=" %%F in ('dir /A-D-H-S /B /S "C:\Temp"') do (
    call :GetSeconds "%%~tF:0"
    if !Seconds! LSS %LastWeek% del /F "%%~fF"
)
endlocal
goto :EOF

:GetSeconds
for /F "tokens=1-6 delims=,-./: " %%A in ("%~1") do (
    set "Day=%%B" & set "Month=%%A" & set "Year=%%C"
    set "Hour=%%D" & set "Minute=%%E" & set "Second=%%F"
)
if "%Month:~0,1%"  EQU "0" ( if "%Month:~1%"  NEQ "" set "Month=%Month:~1%"   )
if "%Day:~0,1%"    EQU "0" ( if "%Day:~1%"    NEQ "" set "Day=%Day:~1%"       )
if "%Hour:~0,1%"   EQU "0" ( if "%Hour:~1%"   NEQ "" set "Hour=%Hour:~1%"     )
if "%Minute:~0,1%" EQU "0" ( if "%Minute:~1%" NEQ "" set "Minute=%Minute:~1%" )
if "%Second:~0,1%" EQU "0" ( if "%Second:~1%" NEQ "" set "Second=%Second:~1%" )
set /A "Index=Year-2014"
for /F "tokens=%Index% delims= " %%Y in ("16436 16801 17167 17532 17897") do set "Days=%%Y"
for /F "tokens=%Index% delims= " %%L in ("N Y N N N") do set "LeapYear=%%L"
if "%LeapYear%" == "N" (
    for /F "tokens=%Month% delims= " %%M in ("0 31 59 90 120 151 181 212 243 273 304 334") do set /A "Days+=%%M"
) else (
    for /F "tokens=%Month% delims= " %%M in ("0 31 60 91 121 152 182 213 244 274 305 335") do set /A "Days+=%%M"
)
set /A "Days+=Day-1"
set /A "Seconds=Days*86400+Hour*3600+Minute*60+Second"
goto :EOF

For even more information about date and time formats and file time comparisons on Windows see my answer on Find out if file is older than 4 hours in batch file with lots of additional information about file times.

@Iman 2011-05-27 08:40:36

Run the following commands:

ROBOCOPY C:\source C:\destination /mov /minage:7
del C:\destination /q

Move all the files (using /mov, which moves files and then deletes them as opposed to /move which moves whole filetrees which are then deleted) via robocopy to another location, and then execute a delete command on that path and you're all good.

Also if you have a directory with lots of data in it you can use /mir switch

@adamb0mb 2013-10-01 22:37:19

For the most part, this is a fairly impractical answer. If I have a directory with lots of data in it, there is will not work well. I'd go with one of the answer that does it "in place"

@syneticon-dj 2013-11-07 08:43:21

@adamb0mb this is in no way impractical - if "destination" is on the same filesystem as "source", the move operation is quite lightweight. As robocopy is really robust, it will in fact work for any directory size, obscure file names, basically arbitrary path lengths and include directories if you need it to - something which surely cannot be said about many other Windows utilities. I would use rd /s /q c:\destination instead of the del command though or even use another robocopy /mir c:\emptydir c:\destination run to empty the directory if you expect trouble with file names.

@syneticon-dj 2013-11-07 08:47:11

Plus, robocopy does support UNC paths - which forfiles from the accepted answer won't do.

@adamb0mb 2013-11-13 00:20:41

My thoughts were more along the lines: "My files are already where I want them. I don't want to have to move them." "Deleting files is logically what you're doing, so do that. Don't overload Robocopy to do it"

@PowerUser 2015-02-04 22:10:59

I recommend this one-liner for viewing all files in C:\test older than 7 days. To delete the files, change the echo to del: forfiles /p "C:\test" /m "*.*" /c "cmd /c echo @file" /D -7

@FearlessFuture 2015-07-15 19:14:58

ROBOCOPY C:\source C:\destination /move /s /minage:7 will copy files in subdirectories.

@AnT 2015-11-21 17:37:16

@PowerUser: Wrong. How do you expect this to see all files, when you explicitly specified a restrictive *.* wildcard? The *.* will not see all files. It will only see files with . in their names. You need * wilcard to see all files, not *.*. Moreover * is the default wildcard win forfiles, meaning that if you want to iterate over everything, parameter /M should be removed entirely.

@Lectrode 2014-01-02 23:04:16

I think e.James's answer is good since it works with unmodified versions of Windows as early as Windows 2000 SP4 (and possibly earlier), but it required writing to an external file. Here is a modified version that does not create an external text file while maintaining the compatibility:

REM del_old.cmd
REM usage: del_old MM-DD-YYYY
setlocal enabledelayedexpansion
for /f "tokens=*" %%a IN ('xcopy *.* /d:%1 /L /I null') do @if exist "%%~nxa" set "excludefiles=!excludefiles!;;%%~nxa;;"
for /f "tokens=*" %%a IN ('dir /b') do @(@echo "%excludefiles%"|FINDSTR /C:";;%%a;;">nul || if exist "%%~nxa" DEL /F /Q "%%a">nul 2>&1)

To be true to the original question, here it is in a script that does ALL the math for you if you call it with the number of days as the parameter:

REM del_old_compute.cmd
REM usage: del_old_compute N
setlocal enabledelayedexpansion
set /a days=%1&set cur_y=%DATE:~10,4%&set cur_m=%DATE:~4,2%&set cur_d=%DATE:~7,2%
for /f "tokens=1 delims==" %%a in ('set cur_') do if "!%%a:~0,1!"=="0" set /a %%a=!%%a:~1,1!+0
set mo_2=28&set /a leapyear=cur_y*10/4
if %leapyear:~-1% equ 0 set mo_2=29
set mo_1=31&set mo_3=31&set mo_4=30&set mo_5=31
set mo_6=30&set mo_7=31&set mo_8=31&set mo_9=30
set mo_10=31&set mo_11=30&set mo_12=31
set /a past_y=(days/365)
set /a monthdays=days-((past_y*365)+((past_y/4)*1))&&set /a past_y=cur_y-past_y&set months=0
:setmonth
set /a minusmonth=(cur_m-1)-months
if %minusmonth% leq 0 set /a minusmonth+=12
set /a checkdays=(mo_%minusmonth%)
if %monthdays% geq %checkdays% set /a months+=1&set /a monthdays-=checkdays&goto :setmonth
set /a past_m=cur_m-months
set /a lastmonth=cur_m-1
if %lastmonth% leq 0 set /a lastmonth+=12
set /a lastmonth=mo_%lastmonth%
set /a past_d=cur_d-monthdays&set adddays=::
if %past_d% leq 0 (set /a past_m-=1&set adddays=)
if %past_m% leq 0 (set /a past_m+=12&set /a past_y-=1)
set mo_2=28&set /a leapyear=past_y*10/4
if %leapyear:~-1% equ 0 set mo_2=29
%adddays%set /a past_d+=mo_%past_m%
set d=%past_m%-%past_d%-%past_y%
for /f "tokens=*" %%a IN ('xcopy *.* /d:%d% /L /I null') do @if exist "%%~nxa" set "excludefiles=!excludefiles!;;%%~nxa;;"
for /f "tokens=*" %%a IN ('dir /b') do @(@echo "%excludefiles%"|FINDSTR /C:";;%%a;;">nul || if exist "%%~nxa" DEL /F /Q "%%a">nul 2>&1)

NOTE: The code above takes into account leap years, as well as the exact number of days in each month. The only maximum is the total number of days there have been since 0/0/0 (after that it returns negative years).

NOTE: The math only goes one way; it cannot correctly get future dates from negative input (it will try, but will likely go past the last day of the month).

@Viktor Ka 2015-07-25 15:37:04

For windows 2012 R2 the following would work:

    forfiles /p "c:\FOLDERpath" /d -30 /c "cmd /c del @path"

to see the files which will be deleted use this

    forfiles /p "c:\FOLDERpath" /d -30 /c "cmd /c echo @path @fdate"

@kwrl 2015-10-12 13:25:54

This even works in Windows Server 2008 and above and Windows 7.

@Sting 2015-04-02 18:58:30

Might I add a humble contribution to this already valuable thread. I'm finding that other solutions might get rid of the actual error text but are ignoring the %ERRORLEVEL% which signals a fail in my application. AND I legitimately want %ERRORLEVEL% just as long as it isn't the "No files found" error.

Some Examples:

Debugging and eliminating the error specifically:

forfiles /p "[file path...]\IDOC_ARCHIVE" /s /m *.txt /d -1 /c "cmd /c del @path" 2>&1 |  findstr /V /O /C:"ERROR: No files found with the specified search criteria."2>&1 | findstr ERROR&&ECHO found error||echo found success

Using a oneliner to return ERRORLEVEL success or failure:

forfiles /p "[file path...]\IDOC_ARCHIVE" /s /m *.txt /d -1 /c "cmd /c del @path" 2>&1 |  findstr /V /O /C:"ERROR: No files found with the specified search criteria."2>&1 | findstr ERROR&&EXIT /B 1||EXIT /B 0

Using a oneliner to keep the ERRORLEVEL at zero for success within the context of a batchfile in the midst of other code (ver > nul resets the ERRORLEVEL):

forfiles /p "[file path...]\IDOC_ARCHIVE" /s /m *.txt /d -1 /c "cmd /c del @path" 2>&1 |  findstr /V /O /C:"ERROR: No files found with the specified search criteria."2>&1 | findstr ERROR&&ECHO found error||ver > nul

For a SQL Server Agent CmdExec job step I landed on the following. I don't know if it's a bug, but the CmdExec within the step only recognizes the first line of code:

cmd /e:on /c "forfiles /p "C:\SQLADMIN\MAINTREPORTS\SQL2" /s /m *.txt /d -1 /c "cmd /c del @path" 2>&1 |  findstr /V /O /C:"ERROR: No files found with the specified search criteria."2>&1 | findstr ERROR&&EXIT 1||EXIT 0"&exit %errorlevel%

@J.R. 2010-07-01 19:38:57

How about this modification on 7daysclean.cmd to take a leap year into account?

It can be done in less than 10 lines of coding!

set /a Leap=0
if (Month GEQ 2 and ((Years%4 EQL 0 and Years%100 NEQ 0) or Years%400 EQL 0)) set /a Leap=day
set /a Months=!_months!+Leap

Edit by Mofi:

The condition above contributed by J.R. evaluates always to false because of invalid syntax.

And Month GEQ 2 is also wrong because adding 86400 seconds for one more day must be done in a leap year only for the months March to December, but not for February.

A working code to take leap day into account - in current year only - in batch file 7daysclean.cmd posted by Jay would be:

set "LeapDaySecs=0"
if %Month% LEQ 2 goto CalcMonths
set /a "LeapRule=Years%%4"
if %LeapRule% NEQ 0 goto CalcMonths
rem The other 2 rules can be ignored up to year 2100.
set /A "LeapDaySecs=day"
:CalcMonths
set /a Months=!_months!+LeapDaySecs

@Tobias Järvelöv 2014-03-14 10:05:30

Expanding on aku's answer, I see a lot of people asking about UNC paths. Simply mapping the unc path to a drive letter will make forfiles happy. Mapping and unmapping of drives can be done programmatically in a batch file, for example.

net use Z: /delete
net use Z: \\unc\path\to\my\folder
forfiles /p Z: /s /m *.gz /D -7 /C "cmd /c del @path"

This will delete all files with a .gz extension that are older than 7 days. If you want to make sure Z: isn't mapped to anything else before using it you could do something simple as

net use Z: \\unc\path\to\my\folder
if %errorlevel% equ 0 (
    forfiles /p Z: /s /m *.gz /D -7 /C "cmd /c del @path"
) else (
    echo "Z: is already in use, please use another drive letter!"
)

@Goran B. 2013-12-19 00:01:20

this is nothing amazing, but i needed to do something like this today and run it as scheduled task etc.

batch file, DelFilesOlderThanNDays.bat below with sample exec w/ params:

DelFilesOlderThanNDays.bat 7 C:\dir1\dir2\dir3\logs *.log

echo off
cls
Echo(
SET keepDD=%1
SET logPath=%2 :: example C:\dir1\dir2\dir3\logs
SET logFileExt=%3
SET check=0
IF [%3] EQU [] SET logFileExt=*.log & echo: file extention not specified (default set to "*.log")
IF [%2] EQU [] echo: file directory no specified (a required parameter), exiting! & EXIT /B 
IF [%1] EQU [] echo: number of days not specified? :)
echo(
echo: in path [ %logPath% ]
echo: finding all files like [ %logFileExt% ]
echo: older than [ %keepDD% ] days
echo(
::
::
:: LOG
echo:  >> c:\trimLogFiles\logBat\log.txt
echo: executed on %DATE% %TIME% >> c:\trimLogFiles\logBat\log.txt
echo: ---------------------------------------------------------- >> c:\trimLogFiles\logBat\log.txt
echo: in path [ %logPath% ] >> c:\trimLogFiles\logBat\log.txt
echo: finding all files like [ %logFileExt% ] >> c:\trimLogFiles\logBat\log.txt
echo: older than [ %keepDD% ] days >> c:\trimLogFiles\logBat\log.txt
echo: ---------------------------------------------------------- >> c:\trimLogFiles\logBat\log.txt
::
FORFILES /p %logPath% /s /m %logFileExt% /d -%keepDD% /c "cmd /c echo @path" >> c:\trimLogFiles\logBat\log.txt 2<&1
IF %ERRORLEVEL% EQU 0 (
 FORFILES /p %logPath% /s /m %logFileExt% /d -%keepDD% /c "cmd /c echo @path"
)
::
::
:: LOG
IF %ERRORLEVEL% EQU 0 (
 echo:  >> c:\trimLogFiles\logBat\log.txt
 echo: deleting files ... >> c:\trimLogFiles\logBat\log.txt
 echo:  >> c:\trimLogFiles\logBat\log.txt
 SET check=1
)
::
::
IF %check% EQU 1 (
 FORFILES /p %logPath% /s /m %logFileExt% /d -%keepDD% /c "cmd /c del @path"
)
::
:: RETURN & LOG
::
IF %ERRORLEVEL% EQU 0 echo: deletion successfull! & echo: deletion successfull! >> c:\trimLogFiles\logBat\log.txt
echo: ---------------------------------------------------------- >> c:\trimLogFiles\logBat\log.txt

@segero 2010-12-14 06:46:25

My command is

forfiles -p "d:\logs" -s -m*.log -d-15 -c"cmd /c del @PATH\@FILE" 

@PATH - is just path in my case, so I had to use @PATH\@FILE

also forfiles /? not working for me too, but forfiles (without "?") worked fine.

And the only question I have: how to add multiple mask (for example ".log|.bak")?

All this regarding forfiles.exe that I downloaded here (on win XP)

But if you are using Windows server forfiles.exe should be already there and it is differs from ftp version. That is why I should modify command.

For Windows Server 2003 I'm using this command:

forfiles -p "d:\Backup" -s -m *.log -d -15 -c "cmd /c del @PATH"

@Arno Jansen 2011-06-08 07:46:04

forfiles /p "v:" /s /m *.* /d -3 /c "cmd /c del @path"

You should do /d -3 (3 days earlier) This works fine for me. So all the complicated batches could be in the trash bin. Also forfiles don't support UNC paths, so make a network connection to a specific drive.

@AnT 2015-11-21 17:38:48

Same error as in other answers (including the accepted one). Where did this strange habit of specifying *.* come from? Wildcard *.* does not match all files in Windows. It only matches files with . in their names. The OP never said anything about requiring . in the file name. The proper parameter is /M *, but this is the default anyway. There's no need for /M at all.

@Andreas Rejbrand 2018-01-11 11:19:23

@AnT: Are you sure? I think *.* behaves exactly like * in Windows. In fact, I just tried it on my computer, and dir *.* indeed listed test file, a file with no extension.

@AnT 2018-01-16 01:58:09

@Andreas Rejbrand: I find it surprising as well, but treatment of *.* is inconsistent between, say, dir and -m option of forfiles. The -m *.* mask will indeed skip extensionless file names, as I stated in my comment above (try it yourself). The funny part here is that MS documentation explicitly states that -m *.* is the default. However, if you try it in real life, you'll see that the default is actually -m * - all files are iterated.

@AnT 2018-01-16 02:04:15

This MS documentation page - technet.microsoft.com/en-us/library/cc753551(v=ws.11).aspx - contains a large number of errors (e.g. in examples) because the author of the doc incorrectly believed that *.* mask applied to all files.

@AnT 2018-01-16 02:40:11

This MS documentation page - technet.microsoft.com/en-us/library/cc753551(v=ws.11).aspx - contains a large number of errors (e.g. in examples) because the author of the doc incorrectly believed that *.* mask applied to all files. My rethorical question about "this strange habit" was indeed uncalled for, since treating *.* and * as equivalent is a long-standing Windows convention. However, /m option in forfiles happens to violate that convention for some reason.

@MSalters 2018-07-02 10:33:58

Background on the 5 wildcards in Windows: blogs.msdn.microsoft.com/jeremykuhne/2017/06/04/…

@Graham Laight 2013-04-26 10:57:48

IMO, JavaScript is gradually becoming a universal scripting standard: it is probably available in more products than any other scripting language (in Windows, it is available using the Windows Scripting Host). I have to clean out old files in lots of folders, so here is a JavaScript function to do that:

// run from an administrator command prompt (or from task scheduler with full rights):  wscript jscript.js
// debug with:   wscript /d /x jscript.js

var fs = WScript.CreateObject("Scripting.FileSystemObject");

clearFolder('C:\\temp\\cleanup');

function clearFolder(folderPath)
{
    // calculate date 3 days ago
    var dateNow = new Date();
    var dateTest = new Date();
    dateTest.setDate(dateNow.getDate() - 3);

    var folder = fs.GetFolder(folderPath);
    var files = folder.Files;

    for( var it = new Enumerator(files); !it.atEnd(); it.moveNext() )
    {
        var file = it.item();

        if( file.DateLastModified < dateTest)
        {
            var filename = file.name;
            var ext = filename.split('.').pop().toLowerCase();

            if (ext != 'exe' && ext != 'dll')
            {
                file.Delete(true);
            }
        }
    }

    var subfolders = new Enumerator(folder.SubFolders);
    for (; !subfolders.atEnd(); subfolders.moveNext())
    {
        clearFolder(subfolders.item().Path);
    }
}

For each folder to clear, just add another call to the clearFolder() function. This particular code also preserves exe and dll files, and cleans up subfolders as well.

@NotJustClarkKent 2012-04-27 22:41:04

For Windows Server 2008 R2:

forfiles /P c:\sql_backups\ /S /M *.sql /D -90 /C "cmd /c del @PATH"

This will delete all .sql files older than 90 days.

@Aidan Ewen 2011-11-14 14:56:03

Use forfiles.

There are different versions. Early ones use unix style parameters.

My version (for server 2000 - note no space after switches)-

forfiles -p"C:\what\ever" -s -m*.* -d<number of days> -c"cmd /c del @path"

To add forfiles to XP, get the exe from ftp://ftp.microsoft.com/ResKit/y2kfix/x86/

and add it to C:\WINDOWS\system32

@Aidan Ewen 2011-11-16 10:30:00

@Kibbee it's not the same answer the syntax is different. This was a geniune attempt to help. Knowning very little of the windows command line, I spent hours of frustration getting this to work. The key bits of info for me were the fact that there's different versions (with different syntax), and that I needed to removed the spaces. Neither of these things were included in the orginal answer. (I would have commented on the answer but I don't have the privileges

@AnT 2018-01-16 02:07:10

Same error as in other answers (including the accepted one). Wildcard *.* in forfiles does not match all files. It only matches files with . in their names (i.e. files with extensions). The OP never said anything about requiring . in the file name. The proper parameter is /M *, but this is the default anyway. There's no need for /M at all.

@Paris 2011-01-26 01:36:21

Copy this code and save it as DelOldFiles.vbs.

USAGE ONLY IN COMMAND PROMPT: cscript DelOldFiles.vbs 15

15 means to delete files older than 15 days in past.

  'copy from here
    Function DeleteOlderFiles(whichfolder)
       Dim fso, f, f1, fc, n, ThresholdDate
       Set fso = CreateObject("Scripting.FileSystemObject")
       Set f = fso.GetFolder(whichfolder)
       Set fc = f.Files
       Set objArgs = WScript.Arguments
       n = 0
       If objArgs.Count=0 Then
           howmuchdaysinpast = 0
       Else
           howmuchdaysinpast = -objArgs(0)
       End If
       ThresholdDate = DateAdd("d", howmuchdaysinpast, Date)   
       For Each f1 in fc
     If f1.DateLastModified<ThresholdDate Then
        Wscript.StdOut.WriteLine f1
        f1.Delete
        n = n + 1    
     End If
       Next
       Wscript.StdOut.WriteLine "Deleted " & n & " file(s)."
    End Function

    If Not WScript.FullName = WScript.Path & "\cscript.exe" Then
      WScript.Echo "USAGE ONLY IN COMMAND PROMPT: cscript DelOldFiles.vbs 15" & vbCrLf & "15 means to delete files older than 15 days in past."
      WScript.Quit 0   
    End If

    DeleteOlderFiles(".")
 'to here

@neuracnu 2010-12-08 00:56:02

If you have the XP resource kit, you can use robocopy to move all the old directories into a single directory, then use rmdir to delete just that one:

mkdir c:\temp\OldDirectoriesGoHere
robocopy c:\logs\SoManyDirectoriesToDelete\ c:\temp\OldDirectoriesGoHere\ /move /minage:7
rmdir /s /q c:\temp\OldDirectoriesGoHere

@e.James 2009-08-24 15:11:15

Have a look at my answer to a similar question:

REM del_old.bat
REM usage: del_old MM-DD-YYY
for /f "tokens=*" %%a IN ('xcopy *.* /d:%1 /L /I null') do if exist %%~nxa echo %%~nxa >> FILES_TO_KEEP.TXT
for /f "tokens=*" %%a IN ('xcopy *.* /L /I /EXCLUDE:FILES_TO_KEEP.TXT null') do if exist "%%~nxa" del "%%~nxa"

This deletes files older than a given date. I'm sure it can be modified to go back seven days from the current date.

update: I notice that HerbCSO has improved on the above script. I recommend using his version instead.

@Paulius 2008-09-09 14:24:49

You might be able to pull this off. You can take a look at this question, for a simpler example. The complexity comes, when you start comparing the dates. It may be easy to tell if the date is greater or not, but there are many situations to consider if you need to actually get the difference between two dates.

In other words - don't try to invent this, unless you really can't use the third party tools.

Related Questions

Sponsored Content

30 Answered Questions

[SOLVED] How do I create a file and write to it in Java?

  • 2010-05-21 19:58:55
  • Drew Johnson
  • 2534235 View
  • 1207 Score
  • 30 Answer
  • Tags:   java file-io

41 Answered Questions

[SOLVED] How to create Excel (.XLS and .XLSX) file in C# without installing Ms Office?

  • 2008-09-29 22:30:28
  • mistrmark
  • 950149 View
  • 1643 Score
  • 41 Answer
  • Tags:   c# .net excel file-io

21 Answered Questions

[SOLVED] Find and restore a deleted file in a Git repository

15 Answered Questions

[SOLVED] How do I pass command line parameters to a batch file?

15 Answered Questions

[SOLVED] Windows batch files: .bat vs .cmd?

10 Answered Questions

[SOLVED] Delete a file or folder

5 Answered Questions

[SOLVED] Long commands split over multiple lines in Windows Vista batch (.bat) file

  • 2008-09-16 03:04:34
  • Dan
  • 327614 View
  • 595 Score
  • 5 Answer
  • Tags:   batch-file cmd

17 Answered Questions

[SOLVED] How do I tell if a regular file does not exist in Bash?

  • 2009-03-12 14:48:43
  • Bill the Lizard
  • 2099016 View
  • 2704 Score
  • 17 Answer
  • Tags:   bash file-io scripting

1 Answered Questions

Batch job to delete folder older than 7 days on windows 7

  • 2016-04-21 05:05:40
  • sharoon d'cunha
  • 2711 View
  • 0 Score
  • 1 Answer
  • Tags:   windows batch-file

Sponsored Content