我正在寻找一种在批处理文件中删除所有早于7天的文件的方法。我在网上搜索过,并发现有些示例代码有数百行,而其他代码则需要安装额外的命令行工具来完成该任务。
在Bash中可以用几行代码完成类似的事情。看起来至少有点简单的东西可以在Windows的批处理文件中完成。我正在寻找在标准的Windows命令提示符中解决问题的解决方案,不需要任何额外的工具。请不要使用PowerShell或Cygwin。
我正在寻找一种在批处理文件中删除所有早于7天的文件的方法。我在网上搜索过,并发现有些示例代码有数百行,而其他代码则需要安装额外的命令行工具来完成该任务。
在Bash中可以用几行代码完成类似的事情。看起来至少有点简单的东西可以在Windows的批处理文件中完成。我正在寻找在标准的Windows命令提示符中解决问题的解决方案,不需要任何额外的工具。请不要使用PowerShell或Cygwin。
享受:
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"
执行以下命令:
ROBOCOPY C:\source C:\destination /mov /minage:7
del C:\destination /q
使用robocopy将所有文件(使用/mov,该选项会移动文件并删除它们,而不是/move选项,该选项会移动整个文件树,然后删除它们)移到另一个位置,然后在该路径上执行删除命令即可。如果您有一个含有大量数据的目录,您可以使用/mir开关。
rd /s /q c:\destination
命令来替代 del
命令,甚至可以运行另一个 robocopy /mir c:\emptydir c:\destination
命令清空目录。 - syneticon-djC:\test
文件夹中7天前的所有文件。如果需要删除这些文件,将echo
改为del
即可: forfiles /p "C:\test" /m "*.*" /c "cmd /c echo @file" /D -7
。 - PowerUser好的,有点无聊,我想出了这个,它包含了我版本的一个贫民Linux纪元替代品,只限于日常使用(没有时间保留):
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
set /a strip=day*7
: 将 7 替换为保留的天数。
set dSource=C:\temp
: 这是要检查文件的起始目录。
此代码不具有破坏性,它将显示本应发生的情况。
更改:
if !epoch! LEQ %slice% (echo DELETE %%f ^(%%~tf^)) ELSE echo keep %%f ^(%%~tf^)
转换为以下的形式:
if !epoch! LEQ %slice% del /f %%f
so files actually get deleted
February: 固定为28天。闰年的处理确实很麻烦,如果有人有想法且不需要添加10行代码,欢迎发表让我可以将其添加到我的代码中。
epoch: 我没有考虑时间因素,因为需要删除早于某个日期的文件,考虑小时/分钟可能会删除一天内应该保留的文件。
epoch 假设你的短日期格式是 YYYY-MM-DD 。需要根据其他设置进行调整或在运行时进行评估(读取 sShortTime ,用户绑定配置,在筛选器中配置正确的字段顺序并使用筛选器从参数中提取正确的数据)。
我提到过我讨厌这个编辑器的自动格式化吗?它会删除空行,复制粘贴非常麻烦。
希望这会有所帮助。
forfiles /p "v:" /s /m *.* /d -3 /c "cmd /c del @path"
你应该执行/d -3
(提前3天)。这对我来说运行良好。因此,所有复杂的批处理都可以丢进垃圾桶。此外,forfiles
不支持UNC路径,所以要建立到特定驱动器的网络连接。
*.*
的奇怪习惯从哪里来?通配符 *.*
在Windows中并不能匹配所有文件,它只能匹配文件名中带有 .
的文件。提问者从未提到需要文件名中的 .
。正确的参数应该是 /M *
,但这已经是默认的了,根本不需要 /M
。 - AnT stands with Russia*.*
的行为与*
完全相同。事实上,我刚刚在我的电脑上尝试了一下,dir *.*
确实列出了一个没有扩展名的文件test file
。 - Andreas Rejbranddir
和 forfiles
的 -m
选项中,对 *.*
的处理是不一致的。就像我在上面的评论中所说的那样,-m *.*
掩码确实会跳过没有扩展名的文件名(你可以自己试一下)。有趣的是,微软的文档明确指出 -m *.*
是默认值。然而,如果你在实际生活中尝试一下,你会发现默认值实际上是 -m *
,也就是会迭代所有文件。 - AnT stands with Russia*.*
掩码适用于 所有 文件。 - AnT stands with Russia*.*
掩码适用于所有文件。我的修辞问题关于“这种奇怪的习惯”确实是不必要的,因为将 *.*
和 *
视为等效是一个长期存在的Windows惯例。然而,forfiles
中的 /m
选项碰巧违反了某些原因的约定。 - AnT stands with RussiaREM 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"
我的命令是
forfiles -p "d:\logs" -s -m*.log -d-15 -c"cmd /c del @PATH\@FILE"
@PATH
- 在我的情况下只是路径,所以我必须使用 @PATH\@FILE
此外,forfiles /?
对我也不起作用,但是没有问号的forfiles
可以正常工作。
我唯一的问题是:如何添加多个掩码(例如 ".log|.bak")?
所有这些都涉及到我从这里下载的 forfiles.exe(在Win XP上)。
但如果您正在使用Windows服务器,则forfiles.exe应该已经存在,并且与ftp版本不同。这就是为什么我需要修改命令的原因。
对于Windows Server 2003,我正在使用以下命令:
forfiles -p "d:\Backup" -s -m *.log -d -15 -c "cmd /c del @PATH"
forfiles /p "c:\FOLDERpath" /d -30 /c "cmd /c del @path"
要查看将被删除的文件,请使用此功能
forfiles /p "c:\FOLDERpath" /d -30 /c "cmd /c echo @path @fdate"
删除3天前的所有文件
forfiles -p "C:\folder" -m *.* -d -3 -c "cmd /c del /q @path"
删除3天前的目录
forfiles -p "C:\folder" -d -3 -c "cmd /c IF @isdir == TRUE rd /S /Q @path"
在批处理中,通常会有与相对日期/时间相关的问题需要解决。但是命令行解释器 cmd.exe 没有针对日期/时间计算的函数。已经在这里、Stack Overflow 的其他页面以及其他网站上发布了许多使用附加控制台应用程序或脚本的良好工作解决方案。
基于日期/时间的操作通常需要将日期/时间字符串转换为自确定日期以来的秒数。非常常见的是 1970-01-01 00:00:00 UTC。但是,根据特定任务所需支持的日期范围,也可以使用任何稍晚的日期。
Jay 发布了包含快速“日期转换为秒”的解决方案的 7daysclean.cmd,适用于命令行解释器 cmd.exe。但它没有正确考虑闰年。J.R. 发布了一个 添加项,用于考虑当前年份的闰日,但忽略自基础年份以来的其他闰年,即自 1970 年以来的其他闰年。
我使用了 20 年静态表格(数组),通过一个小的 C 函数创建一次,用于在我的用 C/C++ 编写的应用程序中快速获取从 1970-01-01 开始包括闰年的天数,在日期/时间转换函数中使用。
这种非常快速的表格方法也可以在使用 FOR 命令的批处理代码中使用。因此,我决定编写批处理子程序 GetSeconds
,用于计算传递给该子程序的日期/时间字符串自 1970-01-01 00:00:00 UTC 以来的秒数。
注意:不考虑闰秒,因为 Windows 文件系统也不支持闰秒。
首先是表格:
自 1970-01-01 00:00:00 UTC 起每年包括闰年的天数。
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
使用1970-01-01作为起始时间戳,计算从2039年到2106年的秒数,只能使用无符号32位变量,即在C/C++中使用unsigned long(或unsigned int)。
但是cmd.exe使用有符号32位变量进行数学表达式计算。因此,最大值为2147483647(0x7FFFFFFF),即2038-01-19 03:14:07。
1970年至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
获取当前年份每个月的第一天距离当天的天数。
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日以来的秒数是很容易的,只需要使用这些表格即可。GetSeconds
的第一次 FOR 循环中分配给环境变量 Day
、Month
和 Year
的令牌的分隔符和顺序。%%~tF
上使用的日期格式不同,则需要调整环境变量的日期字符串。%DATE%
扩展为 Sun 02/08/2015
而 %%~tF
扩展为 02/08/2015 07:38 PM
时,可以使用以下代码并修改第4行为:call :GetSeconds "%DATE:~4% %TIME%"
这导致将日期字符串中的星期缩写和分隔空格字符除外,仅传递02/08/2015
到子例程。
或者,以下内容可用于以正确格式传递当前日期:
call :GetSeconds "%DATE:~-10% %TIME%"
现在,从日期字符串中传递最后10个字符到函数GetSeconds
,因此无论环境变量DATE的日期字符串是否带有星期几,只要天数和月份始终以两位数字的期望顺序呈现,即格式为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系统日期和时间格式以及文件时间比较的信息,请参见我在Find out if file is older than 4 hours in batch file上的回答,其中提供了有关文件时间的大量额外信息。
将以下代码复制并保存为DelOldFiles.vbs
。
在CMD中使用:cscript //nologo DelOldFiles.vbs 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