Windows 批处理文件:FORFILES "date between" 解决方法 - 如何使 >CON 输出可用于 FOR 循环
Windows Batch file: FORFILES "date between" workaround - how to make >CON output available to FOR loop
我创建了一个批处理文件,其中 objective 精简了旧的备份文件。更具体地说,该脚本识别最后修改日期在 183 到 365 天之间的备份文件,在整个 6 个月的时间段内每 7 天删除一个文件以外的所有文件。如果 7 天内有零个或一个文件,则不会删除任何文件。
该脚本基本上可以工作,但依赖于一个临时文件来存储每 7 天的匹配文件的文件名。我想知道是否可以修改脚本以执行相同的操作 而无需 临时文件。
该脚本从 aschipfl's answer 中描述的技术中汲取灵感来解决 FORFILES
设计缺陷。此技术有效地增强了 FORFILES
,因此它可以识别上次修改的文件 在 两个日期(或几天)之间。我看到的困难是 FORFILES
“文件已识别”输出被重定向到 CON
设备。这意味着输出不可用于 FOR /F
循环以进行进一步处理。所以我的“快速修复”是将输出重定向到 FOR /F
循环可以访问的临时文件。我想知道是否可以插入一些文件描述符魔法来利用输出到 FOR
.
这是我的(非破坏性)脚本:
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET BACKUPFILEMASK=*.7z
SET DAYS_OLD_EARLIEST=183
SET DAYS_OLD_LATEST=365
SET TMPFILENAME=thin_out_logging.txt
FOR /L %%A IN (%DAYS_OLD_EARLIEST%,7,%DAYS_OLD_LATEST%) DO (
ECHO Iteration: %%A
SET /A ADDWEEK=%%A+7
>NUL 2>&1 FORFILES /M %BACKUPFILEMASK% /D -%%A /C "CMD /C IF @ISDIR==FALSE 2>NUL FORFILES /M @FILE /D -!ADDWEEK! || >> "%TMP%\%TMPFILENAME%" ECHO @FILE"
FOR /F %%B IN ('TYPE "%TMP%\%TMPFILENAME%" ^| FIND "" /V /C') DO SET /A LINES=%%B
ECHO Lines counted for week: !LINES!
IF !LINES! GEQ 2 (
FOR /F "skip=1 usebackq tokens=*" %%C IN ("%TMP%\%TMPFILENAME%") DO ECHO DEL %%C
)
ECHO ---
BREAK>"%TMP%\%TMPFILENAME%"
)
DEL "%TMP%\%TMPFILENAME%"
经过多次试验,我终于搞定了。使其发挥作用的关键调整是:
- 在
FOR /F
循环中包装 FORFILES
行
- 使用
FOR /F
和 eol="
来抑制打印外部 FORFILES
找到的以 "
开头的不需要的文件。这取代了 >NUL 2>&1
,它也意外地抑制了我想要打印的文件。
- 为我想要打印的文件使用前缀字符,这样它们就不会被
FOR /F
的eol="
选项丢弃.几乎可以使用任何非 "
字符。我选择了波浪字符:ECHO ~DEL @FILE
(行的非破坏性版本)
- 在每次迭代时使用变量进行计数和递增。然后仅在变量为
GEQ
到 2
时打印,以便根据需要跳过第一个找到的文件。这取代了 skip=1
,它在修订后的代码中没有达到预期的效果。
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET BACKUPFILEMASK=*.7z
SET DAYS_OLD_EARLIEST=183
SET DAYS_OLD_LATEST=365
FOR /L %%A IN (%DAYS_OLD_EARLIEST%,7,%DAYS_OLD_LATEST%) DO (
ECHO Iteration: %%A
SET /A ADDWEEK=%%A+7
SET /A COUNT=0
FOR /F ^"eol^=^"^ tokens^=^*^ delims^=^~^" %%B IN ('2^>NUL FORFILES /M %BACKUPFILEMASK% /D -%%A /C ^"CMD /C IF @ISDIR^=^=FALSE 2^>NUL FORFILES /M @FILE /D -!ADDWEEK! ^|^| ECHO ~DEL @FILE^"') DO (
SET /A COUNT+=1
IF !COUNT! GEQ 2 ECHO %%B
)
ECHO ---
)
示例输出:
Iteration: 183
DEL "Notes_backup_18112020_0837.7z"
DEL "Notes_backup_19112020_0832.7z"
DEL "Notes_backup_20112020_0844.7z"
DEL "Notes_backup_21112020_0955.7z"
DEL "Notes_backup_22112020_1339.7z"
DEL "Notes_backup_23112020_0941.7z"
---
Iteration: 190
DEL "Notes_backup_11112020_0907.7z"
DEL "Notes_backup_12112020_0930.7z"
DEL "Notes_backup_13112020_0844.7z"
DEL "Notes_backup_14112020_0916.7z"
DEL "Notes_backup_15112020_1022.7z"
DEL "Notes_backup_16112020_0905.7z"
---
要真正删除这些文件,而不是仅仅将 DEL
命令打印到控制台,请将 ECHO ~DEL
替换为 ECHO ~
,并将 IF !COUNT! GEQ 2 ECHO %%B
替换为 IF !COUNT! GEQ 2 DEL %%B
.
可以理解,除我以外的任何人都可能难以测试,因为需要一系列具有正确修改日期的每日备份文件。因此,如果有人有兴趣对此进行测试,这里有一些 Powershell(多么讽刺)可以创建测试文件,并将修改日期设置为过去 365 天内的每一天:
for($i=1; $i -le 365; $i++)
{
$date = (Get-Date).AddDays(-1-$i)
$filedate = $date.ToString('yyyy-MM-dd')
$file = New-Item -Path . -Name "${i}.7z" -ItemType File
$file.LastWriteTime = $date
}
要使用,请将代码保存到某处的 .ps1
文件中,将 cd
保存到您要创建文件的目录中,然后 运行 Powershell 输入类似 powershell -ExecutionPolicy ByPass -File C:\path\to\script.ps1
.
我创建了一个批处理文件,其中 objective 精简了旧的备份文件。更具体地说,该脚本识别最后修改日期在 183 到 365 天之间的备份文件,在整个 6 个月的时间段内每 7 天删除一个文件以外的所有文件。如果 7 天内有零个或一个文件,则不会删除任何文件。
该脚本基本上可以工作,但依赖于一个临时文件来存储每 7 天的匹配文件的文件名。我想知道是否可以修改脚本以执行相同的操作 而无需 临时文件。
该脚本从 aschipfl's answer 中描述的技术中汲取灵感来解决 FORFILES
设计缺陷。此技术有效地增强了 FORFILES
,因此它可以识别上次修改的文件 在 两个日期(或几天)之间。我看到的困难是 FORFILES
“文件已识别”输出被重定向到 CON
设备。这意味着输出不可用于 FOR /F
循环以进行进一步处理。所以我的“快速修复”是将输出重定向到 FOR /F
循环可以访问的临时文件。我想知道是否可以插入一些文件描述符魔法来利用输出到 FOR
.
这是我的(非破坏性)脚本:
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET BACKUPFILEMASK=*.7z
SET DAYS_OLD_EARLIEST=183
SET DAYS_OLD_LATEST=365
SET TMPFILENAME=thin_out_logging.txt
FOR /L %%A IN (%DAYS_OLD_EARLIEST%,7,%DAYS_OLD_LATEST%) DO (
ECHO Iteration: %%A
SET /A ADDWEEK=%%A+7
>NUL 2>&1 FORFILES /M %BACKUPFILEMASK% /D -%%A /C "CMD /C IF @ISDIR==FALSE 2>NUL FORFILES /M @FILE /D -!ADDWEEK! || >> "%TMP%\%TMPFILENAME%" ECHO @FILE"
FOR /F %%B IN ('TYPE "%TMP%\%TMPFILENAME%" ^| FIND "" /V /C') DO SET /A LINES=%%B
ECHO Lines counted for week: !LINES!
IF !LINES! GEQ 2 (
FOR /F "skip=1 usebackq tokens=*" %%C IN ("%TMP%\%TMPFILENAME%") DO ECHO DEL %%C
)
ECHO ---
BREAK>"%TMP%\%TMPFILENAME%"
)
DEL "%TMP%\%TMPFILENAME%"
经过多次试验,我终于搞定了。使其发挥作用的关键调整是:
- 在
FOR /F
循环中包装FORFILES
行 - 使用
FOR /F
和eol="
来抑制打印外部FORFILES
找到的以"
开头的不需要的文件。这取代了>NUL 2>&1
,它也意外地抑制了我想要打印的文件。 - 为我想要打印的文件使用前缀字符,这样它们就不会被
FOR /F
的eol="
选项丢弃.几乎可以使用任何非"
字符。我选择了波浪字符:ECHO ~DEL @FILE
(行的非破坏性版本) - 在每次迭代时使用变量进行计数和递增。然后仅在变量为
GEQ
到2
时打印,以便根据需要跳过第一个找到的文件。这取代了skip=1
,它在修订后的代码中没有达到预期的效果。
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET BACKUPFILEMASK=*.7z
SET DAYS_OLD_EARLIEST=183
SET DAYS_OLD_LATEST=365
FOR /L %%A IN (%DAYS_OLD_EARLIEST%,7,%DAYS_OLD_LATEST%) DO (
ECHO Iteration: %%A
SET /A ADDWEEK=%%A+7
SET /A COUNT=0
FOR /F ^"eol^=^"^ tokens^=^*^ delims^=^~^" %%B IN ('2^>NUL FORFILES /M %BACKUPFILEMASK% /D -%%A /C ^"CMD /C IF @ISDIR^=^=FALSE 2^>NUL FORFILES /M @FILE /D -!ADDWEEK! ^|^| ECHO ~DEL @FILE^"') DO (
SET /A COUNT+=1
IF !COUNT! GEQ 2 ECHO %%B
)
ECHO ---
)
示例输出:
Iteration: 183
DEL "Notes_backup_18112020_0837.7z"
DEL "Notes_backup_19112020_0832.7z"
DEL "Notes_backup_20112020_0844.7z"
DEL "Notes_backup_21112020_0955.7z"
DEL "Notes_backup_22112020_1339.7z"
DEL "Notes_backup_23112020_0941.7z"
---
Iteration: 190
DEL "Notes_backup_11112020_0907.7z"
DEL "Notes_backup_12112020_0930.7z"
DEL "Notes_backup_13112020_0844.7z"
DEL "Notes_backup_14112020_0916.7z"
DEL "Notes_backup_15112020_1022.7z"
DEL "Notes_backup_16112020_0905.7z"
---
要真正删除这些文件,而不是仅仅将 DEL
命令打印到控制台,请将 ECHO ~DEL
替换为 ECHO ~
,并将 IF !COUNT! GEQ 2 ECHO %%B
替换为 IF !COUNT! GEQ 2 DEL %%B
.
可以理解,除我以外的任何人都可能难以测试,因为需要一系列具有正确修改日期的每日备份文件。因此,如果有人有兴趣对此进行测试,这里有一些 Powershell(多么讽刺)可以创建测试文件,并将修改日期设置为过去 365 天内的每一天:
for($i=1; $i -le 365; $i++)
{
$date = (Get-Date).AddDays(-1-$i)
$filedate = $date.ToString('yyyy-MM-dd')
$file = New-Item -Path . -Name "${i}.7z" -ItemType File
$file.LastWriteTime = $date
}
要使用,请将代码保存到某处的 .ps1
文件中,将 cd
保存到您要创建文件的目录中,然后 运行 Powershell 输入类似 powershell -ExecutionPolicy ByPass -File C:\path\to\script.ps1
.