批处理删除过期文件夹

今天在百度知道看到这样一个问题:

批处理只删除一个众多的文件夹里,昨天的文件夹?
http://zhidao.baidu.com/question/689168739848712772

我在该问题下做了简短回答并分享了我以前整理的代码;

细想这个问题,以及当初遇到同样需求时的处理过程,就想回过头来再掰扯掰扯;

掰扯的目的是为了能够理解批处理如何实现批量删除过期文件(夹)的过程。

翻开我的笔记,记录日期停留在2019年10月16日。

当时遇到的问题是公司一个项目每天的备份计划失败了,查看原因,原来是硬盘空间满了导致备份失败;

后来增加了NAS对每天备份的数据进行同步,但服务器自身的备份数据在同步后仍留在硬盘上,有什么办法能够对这些历史数据进行批量删除,仅保留最近60天的数据呢?

对于项目所在的Windows操作系统,我想通过 计划任务+批处理 的组合,来完成这项任务。

声明:

任何因使用本博文内的任何代码造成的任何损失或危害,都与本人无关,继续阅读说明您已知晓此声明!

1、分析问题

昨天的文件夹、仅保留最近60天的数据(或者理解为删除超过60天的数据),这些表述的文件(夹)正如标题中拟定的“过期”之意。

怎么筛选出这些过期的文件(夹)?

需要注意,这个过期是针对文件(夹)的创建日期还是修改日期?

相对于我这个项目备份生成的数据,一旦备份生成就不会再动,其创建日期和修改日期一致。

所以需要具体情况具体对待。

2、解决问题

2.1 Forfiles

这是最初找到的适合于在批处理中操作的一个命令。

其帮助信息点击Microsoft Doc:Forfiles,可进一步了解,这里给几个示例:

forfiles /p "e:\bak\project1" /s /d -60 /c "CMD /C ECHO @path"

这句的作用是列出E盘bak\project1目录(/p)及子目录(/s)下,修改日期至今超过60天(/d -60)的所有文件(夹),并输出文件的完整路径。

forfiles /p "e:\bak\project1" /s /d -60 /c "CMD /C IF @ISDIR==TRUE ECHO @PATH"

这句话在前面一句命令上,加了个@ISDIR==TRUE判断(实测 TRUE 必须大写)

作用是仅输出所有符合条件的文件夹的路径

至此,要删除超过60天的历史数据,似乎有了办法

REM 筛选出超过60天的文件夹信息放入临时文件expired_dirs.txt
forfiles /p "e:\bak\project1" /s /d -60 /c "CMD /C IF @ISDIR==TRUE ECHO @PATH" | sort /r > e:\expired_dirs.txt

REM 逐条读取信息并执行静默删除
REM RD删除命令很危险,测试时最好用echo命令多尝试,确认后使用RD
For /f "tokens=*" %%i in (d:\expired_dirs.txt) DO RD /S /Q "%%i"

 

那么,只想删除昨天的文件夹,怎么处理?

forfiles /p "e:\bak\project1" /s /d -1 /c "CMD /C IF @ISDIR==TRUE ECHO @PATH"

这条命令能不能只筛选出昨天的文件夹?

肯定不行,这句的作用只是列出早于昨天(/d -1)的所有文件夹。

 

那么,批处理里,怎么处理像昨天、前天这种特指某一天的数据呢?

 

2.2 儒略日

任何操作系统都要有一套时间格式,但似乎并没有真正的标准,而儒略日是天文学中的一种时间测量方法,其以公元前4713年1月1日UTC的中午为起始测量时间点,并使用浮点值以天为单位测量时间。其近乎于标准!

例如今天(2021-4-16)的儒略日为2459321,其计算公式:

JDN = (1461 × (Y + 4800 + (M − 14)/12))/4 +(367 × (M − 2 − 12 × ((M − 14)/12)))/12 − (3 × ((Y + 4900 + (M - 14)/12)/100))/4 + D − 32075

批处理实现的代码片断:

REM 返回给定日期的儒略日(Julian Day Number)

REM 需注意系统日期的格式 yyyy/mm/dd

:DateToJDN "yyyy/mm/dd DD" jdn=

setlocal

set date=%~1

set /A yy=%date:~0,4%, mm=1%date:~5,2% %% 100, dd=1%date:~8,2% %% 100

set /A a=mm-14, jdn=(1461*(yy+4800+a/12))/4+(367*(mm-2-12*(a/12)))/12-(3*((yy+4900+a/12)/100))/4+dd-32075

endlocal & set %2=%jdn%
exit /B

以上代码片断的作用是定义一个DateToJDN函数,用以转换输入时间所对应的儒略日

SET TODAY=%date:~0,10%

CALL :DateToJDN "%TODAY%" JDNToday

echo !JDNToday!

执行上面的代码,可以计算出今天(2021-4-16)的儒略日为2459321

那么昨天的儒略日为2459320,前天的儒略日为2459319,这很好理解吧!

反过来,如果要筛选出昨天的文件夹,只要将文件夹的创建日期转换成儒略日,并计算与今天对应的儒略日之间的差值是否为1。

2.3 基于儒略日的代码实现

dir /ad /tc "e:\bak\project1"

列出E盘bak\project1目录下的所有目录信息,输出示例:

 驱动器 E 中的卷没有标签。
 卷的序列号是 1911-FB58

 E:\bak\project1 的目录

2021/03/09  11:24    <DIR>          .
2021/03/09  11:24    <DIR>          ..
2021/03/09  11:24    <DIR>          20210309
2021/03/10  21:15    <DIR>          20210310
2021/03/11  21:15    <DIR>          20210311
2021/03/12  21:15    <DIR>          20210312
2021/03/13  21:15    <DIR>          20210313
......
2021/04/13  21:15    <DIR>          20210413
2021/04/14  21:15    <DIR>          20210414
2021/04/15  21:15    <DIR>          20210415

清理输出结果中的无用信息

dir /ad /tc "e:\bak\project1"^| findstr "^[0-9]"
2021/03/09  11:24    <DIR>          .
2021/03/09  11:24    <DIR>          ..
2021/03/09  11:24    <DIR>          20210309
2021/03/10  21:15    <DIR>          20210310
2021/03/11  21:15    <DIR>          20210311
2021/03/12  21:15    <DIR>          20210312
2021/03/13  21:15    <DIR>          20210313
......
2021/04/13  21:15    <DIR>          20210413
2021/04/14  21:15    <DIR>          20210414
2021/04/15  21:15    <DIR>          20210415

现在,怎么逐条拿出每个文件夹的日期值并转换成儒略日以进行差值计算?这里要用到For命令

SET FOLDER=E:\bak\project1

SET TODAY=%date:~0,10%
REM 取今日的儒略日 赋值给 JDNToday
CALL :DateToJDN "%TODAY%" JDNToday

REM 循环取 E:\bak\project1 目录下 各子目录的 创建日期
echo 创建日期  儒略日  差值
FOR /f "tokens=1,4*" %%I IN ('dir /ad /tc "%FOLDER%"^| findstr "^[0-9]"') DO (
   	REM 取子目录的儒略日 赋值给 filedate
	CALL :DateToJDN "%%I" filedate

	REM 计算与今日的儒略日之间的差值
	SET /a diffdays=JDNToday-filedate

	echo %%I !filedate! !diffdays!

)

上面的代码,会输出下面的结果:

创建日期  儒略日  差值
2021/03/09 2459283 38
2021/03/09 2459283 38
2021/03/09 2459283 38
2021/03/10 2459284 37
2021/03/11 2459285 36
2021/03/12 2459286 35
2021/03/13 2459287 34
......
2021/04/13 2459318 3
2021/04/14 2459319 2
2021/04/15 2459320 1

输出的结果给出了各子目录的创建日期与今日的儒略日之间的差值。

最后,要取昨天的文件夹,就要判断差值是否等于1

简单地补充一下,批处理中的关系运算符:

EQU(=), NEQ(<>), LSS(<), LEQ(<=), GTR(>), GEQ(>=)

IF !diffdays! EQU 1 ECHO "这是昨天创建的文件夹"

相关的点是不是都齐了?!最后我们整理一下完整 的代码:

@ECHO OFF & SETLOCAL ENABLEDELAYEDEXPANSION

REM 删除指定天数前创建的目录(所有子目录及文件)
REM 指定维护的目录
SET FOLDER=%TEMP%

REM 设置超限天数
SET /a XDay= 1

REM 记录日志
SET SHECULED_LOG_DIR=%TEMP%
IF NOT EXIST %SHECULED_LOG_DIR% MKDIR %SHECULED_LOG_DIR%

SET TODAY=%date:~0,10%
SET DATE_STR=%TODAY:/=%
SET LOG=%SHECULED_LOG_DIR%\rd-expired-directories-%DATE_STR%.LOG

rem echo Start ------ (%date% %time%) ------ >> %LOG%
rem echo Get All Expired Directories >> %LOG%
CALL :DateToJDN "%TODAY%" JDNToday

FOR /d %%a in ("%FOLDER%\*") DO (
  FOR /f "tokens=1,4*" %%I IN ('dir /ad /tc "%%~a"^|findstr "^[0-9]"') DO (
    CALL :DateToJDN "%%I" filedate
    SET /a diffdays=JDNToday-filedate
    REM echo %%I !filedate! !diffdays!
    REM 删除指令: RD /S /D
    REM 在确认安全的前提下,替换下方的 ECHO
    IF !diffdays! EQU %XDay% ECHO "%%~a\%%~J" >> %LOG%

  )
)

rem echo Done ------ (%date% %time%) ------  >> %LOG%
GOTO :eof

REM 返回给定日期的儒略日(Julian Day Number)
REM https://en.wikipedia.org/wiki/Julian_day
:DateToJDN "yyyy/mm/dd DD" jdn=
  setlocal
  set date=%~1
  set /A yy=%date:~0,4%, mm=1%date:~5,2% %% 100, dd=1%date:~8,2% %% 100
  set /A a=mm-14, jdn=(1461*(yy+4800+a/12))/4+(367*(mm-2-12*(a/12)))/12-(3*((yy+4900+a/12)/100))/4+dd-32075
  endlocal & set %2=%jdn%
exit /B

注:

其他的辅助性说明已在完整代码里进行注释;

增加了日志记录,日志文件位于系统临时目录(%TEMP%);

RD /S /Q 是静默方式的递归删除命令,须严格确认安全的前提下再使用;

补充:
如要通过该段代码取60天前的数据,怎么操作?
其实只要修改两处:
1. 修改xDay = 60
2. 修改关系运行符EQU => GTR

  IF !diffdays! GTR %XDay% ECHO "%%~a\%%~J" >> %LOG%

[via]

https://docs.microsoft.com/zh-cn/windows-server/administration/windows-commands/

https://en.wikipedia.org/wiki/Julian_day

发表评论

电子邮件地址不会被公开。 必填项已用*标注