Batch file to delete files older than N days

前端 未结 24 2449
没有蜡笔的小新
没有蜡笔的小新 2020-11-21 11:11

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 oth

相关标签:
24条回答
  • 2020-11-21 11:41

    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

    0 讨论(0)
  • 2020-11-21 11:42

    Copy this code and save it as DelOldFiles.vbs.

    USAGE IN CMD : cscript //nologo 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
    
    0 讨论(0)
  • 2020-11-21 11:43

    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.

    0 讨论(0)
  • 2020-11-21 11:43

    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
    
    0 讨论(0)
  • 2020-11-21 11:43

    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%
    
    0 讨论(0)
  • 2020-11-21 11:43

    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
    
    0 讨论(0)
提交回复
热议问题