我正在寻找一种方法来删除批处理文件中超过7天的所有文件。我在网上搜索了一下,找到了一些包含数百行代码的示例,还有一些示例需要安装额外的命令行实用程序来完成任务。

在BASH中,只需几行代码就可以完成类似的工作。在Windows中,似乎可以为批处理文件做一些简单的事情。我正在寻找一个解决方案,工作在标准的Windows命令提示符,没有任何额外的实用程序。请不要使用PowerShell或Cygwin。


当前回答

我认为e.James的答案很好,因为它适用于早于Windows 2000 SP4(可能更早)的未修改版本的Windows,但它需要写入外部文件。下面是一个修改后的版本,在保持兼容性的同时不创建外部文本文件:

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)

为了忠于最初的问题,如果你用天数作为参数调用它,它在一个脚本中为你做了所有的数学运算:

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)

注意:上面的代码考虑了闰年,以及每个月的确切天数。唯一的最大值是从0/0/0开始的总天数(之后返回负年份)。

注意:数学只是单向的;它不能从负输入中正确地获得未来的日期(它会尝试,但可能会超过本月的最后一天)。

其他回答

享受:

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

更多细节请参见forfiles文档。

要了解更多信息,请参考Windows XP命令行的A-Z索引。

如果您的计算机上没有安装forfiles,请将其从任何Windows Server 2003复制到您的Windows XP计算机的%WinDir%\system32\。这是可能的,因为EXE在Windows Server 2003和Windows XP之间完全兼容。

后续版本的Windows和Windows Server默认安装了它。

Windows 7及更新版本(包括Windows 10):

语法略有变化。因此更新后的命令为:

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

批处理文件经常需要解决与日期/时间相关的问题。但是命令行解释器cmd.exe没有计算日期/时间的功能。在Stack Overflow的其他页面和其他网站上已经发布了许多使用附加控制台应用程序或脚本的良好工作解决方案。

基于日期/时间的操作通常需要将日期/时间字符串转换为从确定日期开始的秒数。非常常见的是1970-01-01 00:00:00 UTC。但是也可以根据支持特定任务所需的日期范围使用任何稍后的日期。

杰伊发布了7天清洁。CMD包含一个快速的“日期到秒”解决方案的命令行解释器CMD .exe。但它没有考虑闰年的正确。J.R.发布了一个附加组件,将当年的闰日考虑在内,但忽略了基准年以来的其他闰年,即1970年以来的闰年。

我使用自20年来的静态表(数组),用一个小的C函数创建一次,以便快速获得从1970-01-01开始的日期/时间转换函数中的天数,包括闰日,在我用C/ c++编写的应用程序中。

这种非常快速的表方法也可以在批处理代码中使用FOR命令。因此,我决定编写批处理子例程GetSeconds,该子例程为传递给该例程的日期/时间字符串计算自1970-01-01 00:00:00 UTC以来的秒数。

注意:此处不考虑闰秒,因为Windows文件系统也不支持闰秒。

首先是表格:

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. 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 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

使用这些表将日期转换为1970-01-01以来的秒数非常容易。

请注意!

日期和时间字符串的格式取决于Windows地区和语言设置。在GetSeconds的第一个FOR循环中,分配给环境变量Day、Month和Year的分隔符和令牌顺序必须在必要时适应本地日期/时间格式。

如果环境变量date中的日期格式与%%~tF上FOR命令使用的日期格式不同,则需要调整环境变量的日期字符串。

例如,当%DATE%扩展到Sun 02/08/2015,而%%~tF扩展到02/08/2015 07:38 PM,下面的代码可以通过修改第4行来使用:

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

这将导致只传递给子例程02/08/2015 -日期字符串,不包含3个字母的工作日缩写和分隔空格字符。

或者可以使用以下方法以正确的格式传递当前日期:

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

现在日期字符串的最后10个字符被传递给函数GetSeconds,因此环境变量date的日期字符串是否有工作日并不重要,只要日和月总是以预期的顺序为2位数字,即格式为dd/mm/yyyy或dd.mm.yyyy。

下面是带有解释注释的批处理代码,它只是输出要删除的文件和要保留在C:\Temp文件夹树中的文件,请参阅第一个FOR循环的代码。

@echo off
setlocal EnableExtensions DisableDelayedExpansion
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=" %%# in ('dir /A-D-H-S /B /S "C:\Temp"') do (
    call :GetSeconds "%%~t#:0"
    set "FullFileName=%%#"
    setlocal EnableDelayedExpansion
    rem if !Seconds! LSS %LastWeek% del /F "!FullFileName!"
    if !Seconds! LEQ %LastWeek% (
        echo Delete "!FullFileName!"
    ) else (
        echo Keep   "!FullFileName!"
    )
    endlocal
)
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 not "%DateTime: AM=%" == "%DateTime%" (
    set "DateTime=%DateTime: AM=%"
) else if not "%DateTime: PM=%" == "%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%"  == "0" if not "%Month:~1%"  == "" set "Month=%Month:~1%"
if "%Day:~0,1%"    == "0" if not "%Day:~1%"    == "" set "Day=%Day:~1%"
if "%Hour:~0,1%"   == "0" if not "%Hour:~1%"   == "" set "Hour=%Hour:~1%"
if "%Minute:~0,1%" == "0" if not "%Minute:~1%" == "" set "Minute=%Minute:~1%"
if "%Second:~0,1%" == "0" if not "%Second:~1%" == "" 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 two 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

为了获得最佳性能,最好删除所有注释,即0-4个前导空格后以rem开头的所有行。

而且数组也可以做得更小,即减少时间范围从1980-01-01 00:00:00到2038-01-19 03:14:07,就像上面的批处理代码目前所支持的那样,例如从2015-01-01到2019-12-31,就像下面的代码所使用的那样,它会真正删除C:\Temp文件夹树中超过7天的文件。

此外,下面的批处理代码针对24小时时间格式进行了优化。

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

for /F "delims=" %%# in ('dir /A-D-H-S /B /S "C:\Temp"') do (
    call :GetSeconds "%%~t#:0"
    set "FullFileName=%%#"
    setlocal EnableDelayedExpansion
    if !Seconds! LSS %LastWeek% del /F "!FullFileName!"
    endlocal
)
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%"  == "0" if not "%Month:~1%"  == "" set "Month=%Month:~1%"
if "%Day:~0,1%"    == "0" if not "%Day:~1%"    == "" set "Day=%Day:~1%"
if "%Hour:~0,1%"   == "0" if not "%Hour:~1%"   == "" set "Hour=%Hour:~1%"
if "%Minute:~0,1%" == "0" if not "%Minute:~1%" == "" set "Minute=%Minute:~1%"
if "%Second:~0,1%" == "0" if not "%Second:~1%" == "" 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

有关Windows上的日期和时间格式以及文件时间比较的更多信息,请参阅我的回答:在批处理文件中查找文件是否超过4小时,并提供大量关于文件时间的附加信息。

看看我对类似问题的回答:

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"

这将删除比给定日期更早的文件。我相信它可以被修改为从当前日期往回走7天。

更新:我注意到HerbCSO已经改进了上面的脚本。我建议改用他的版本。

复制这段代码并保存为DelOldFiles.vbs。

使用CMD: cscript //nologo DelOldFiles。于六月15日

“15”表示删除超过15天的文件。

  '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

这个在7daysclean上的修改怎么样?CMD要把闰年考虑进去吗?

它可以在不到10行代码中完成!

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

Mofi编辑:

由于语法无效,由J.R.提供的上述条件总是计算为false。

GEQ 2月份也是错误的,因为在闰年里,只有3月到12月必须增加86400秒来增加一天,而2月则不行。

在批处理文件7天清理中考虑闰日的工作代码-仅在当前年份。Jay发布的cmd将是:

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