当前位置: 首页 > 知识库问答 >
问题:

Bat文件仅当存在较年轻的文件时才删除文件

司马彬
2023-03-14

我们的备份系统创建。bak文件,我们可以使用它来恢复文件,如果我们遇到问题。如果不处理,这些文件将填满我们的存储空间,因此我找到了一个批处理文件,每天创建新的批处理文件后,我都可以运行该文件来删除旧的批处理文件。

文件-p“c:\xxx\yyy”-s-m*。bak/D-2/C“cmd/C del@path”

这工作正常,但我想创建一个安全网,以便无论出于何种原因,我们的备份系统出现故障并且没有创建新的. bak文件,旧的. bak文件将保留在那里而不是被删除,否则我们将在事件发生时没有备份文件。所以理想情况下,我想要一些可以检查小于一天的. bak文件的东西,如果这些文件不存在,它不会运行上面的行,但如果存在这些较年轻的文件,它将运行上面的行并删除较旧的文件。不确定批处理文件是否可以做到这一点。提前感谢您对此的帮助。

编辑:关于我需要什么的更多信息。每天晚上10点左右,大约50次备份。bak文件被创建并放入文件夹c:\xxx\yyy。这些文件非常大,所以我设置了一个批处理文件,每天自动运行,删除所有。bak早于1天的文件。这对于日常使用来说很好,但我脑海中的场景是,如果备份系统没有创建<代码>。无论出于何种原因,bak都会归档。我想检查批处理文件,以确保新的。bak文件在删除旧文件之前已创建。基本上,使用批处理文件可以检查文件夹中是否存在比1天新的特定文件类型,并且我们可以根据结果更改批处理文件的功能。

这些是为18日和19日创建的文件示例

2004 Apr_backup_2017_12_18_210001_2986007.bak
2004 Apr_backup_2017_12_19_210001_3168635.bak
Subscribers_backup_2017_12_19_210003_3012893.bak
model_backup_2017_12_19_210003_2544131.bak

它们似乎都遵循以下格式:

[DESC]_backup_[YEAR]_[MONTH]_[DAY]_21000[1/2/3]_[7 DIGIT NO.].bak

共有2个答案

吴凯泽
2023-03-14

这里有一个未经测试的示例脚本,只要您没有文件名,其中包含\ucode>、=或其他有问题的字符,它就可以工作。

@Echo Off
SetLocal DisableDelayedExpansion
For /F "Delims==" %%A In ('Set _[ 2^>Nul') Do Set "%%A="
If /I Not "%CD%"=="C:\xxx\yyy" (Set "_[:]=T"
    PushD "C:\xxx\yyy" 2>Nul||Exit /B)
For /F "Tokens=1* Delims=_" %%A In ('Dir /B /O-N *_backup_*_*_*_*_*.bak'
) Do If Defined _[%%A] (Del /A /F "%%A_%%B") Else Set "_[%%A]=T"
If Defined _[:] PopD
EndLocal
Exit /B

卢志行
2023-03-14

我认为,所有备份文件名中未知的[DESC]字符串列表在批处理文件中最难处理。代码可能非常简单,因为它可以在下面看到,或者至少知道这些字符串是否不包含对批处理文件(如! %=)至关重要的字符。

但文件名中带有特殊字符的未知字符串列表的编码挑战对我来说很有趣,因此我首先开发了以下注释批处理文件:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "BackupFolder=C:\xxx\yyy"

rem Search for files matching the wildcard pattern *_backup_*.bak in backup
rem folder, assign each file name without file extension to environment
rem variable FileName and call the subroutine GetUniqueDescs to get the
rem file description at beginning of each file name into a list in memory.

for /F "delims=" %%I in ('dir "%BackupFolder%\*_backup_*.bak" /A-D /B /ON 2^>nul') do (
    set "FileName=%%~nI"
    call :GetUniqueDescs
)

rem Run command SET with FileDesc: to output all environment variables
rem starting with that string in name and sorted by name and process
rem this list whereby each line ends with =1 as value 1 is assigned
rem to each of these environment variables.

rem For each unique file description in output list assign the file
rem description with =1 appended to environment variable FileDesc
rem and run subroutine DeleteFiles.

for /F "tokens=2 delims=:" %%I in ('set FileDesc: 2^>nul') do (
    set "FileDesc=%%I"
    call :DeleteFiles
)

rem Restore initial environment on starting this batch file and exit it.
endlocal
goto :EOF


rem The subroutine GetUniqueDescs first runs a string substitution which
rem gets the backup pattern part from file name, i.e. everything in file
rem name from _backup_ to end of file name.

rem Then another string substitution is used to remove this string from
rem current file name to get just the description and define an environment
rem variable of which name starts with FileDesc: and ends with the file
rem description. The value assigned to this environment variable is 1.

:GetUniqueDescs
set "BackupPart=%FileName:*_backup_=_backup_%"
call set "FileDesc:%%FileName:%BackupPart%=%%=1"
goto :EOF


rem The subroutine DeleteFiles removes first from passed file description
rem the last two characters being always =1 from list of environment
rem variables starting with FileDesc: and appends the backup wildcard
rem pattern.

rem Command DIR is used to find all files in backup folder starting
rem with current file description and _backup_ and output the found
rem files sorted by last modification date with newest modified file
rem first and oldest modified file last.

rem The command FOR processing this list skips the first file name
rem output by DIR which means the newest file. All other, older
rem files perhaps also found by DIR are deleted one after the other.

:DeleteFiles
set "FilePattern=%FileDesc:~0,-2%_backup_*.bak"
for /F "skip=1 delims=" %%J in ('dir "%BackupFolder%\%FilePattern%" /A-D /B /O-D /TW') do ECHO del "%BackupFolder%\%%J"
goto :EOF

在命令del之前的最后一行中的命令ECHO只显示要删除的文件,而不是真正删除它们。

最后但有一行的选项skip=1确定始终保留多少备份文件。

例如,使用skip=5将导致根据上次修改日期保留最新的五个文件,通常在备份文件上,也就是创建日期,并删除所有其他文件。

这种备份删除策略的优点在于,它并不重要:

  1. 创建特定备份的频率—每天、每周或每月

删除备份真正重要的是每个备份所需的存储大小以及删除过程后剩余的可用存储空间。备份文件的文件日期不限制可用存储大小。所有剩余备份文件的文件大小以及备份介质上的总存储大小才是真正重要的因素。这就是为什么我不理解所有那些“删除早于”的问题。只要有足够的可用空间来存放新文件,谁还需要关心文件的年龄?

文件创建日期也可以通过在最后一行中使用TC而不是TW来使用。但文件创建日期是在该目录中创建文件的日期,而不是创建文件本身的日期。因此,只有当文件自第一次创建以来从未复制或移动到其他目录时,文件创建日期才有用。

我在以下文件上测试了此批处理文件:

C:\xxx\yyy\2004 !Apr_backup_2017_12_18_210001_2986007.bak
C:\xxx\yyy\2004 !Apr_backup_2017_12_19_210001_3168635.bak
C:\xxx\yyy\model%_backup_2017_12_19_210003_2544131.bak
C:\xxx\yyy\model%_backup_2017_12_20_210003_2544131.bak
C:\xxx\yyy\Subscribers=_backup_2017_12_19_210003_3012893.bak
C:\xxx\yyy\Subscribers=_backup_2017_12_20_210003_3012893.bak

每个文件的最后修改日期与文件名中的日期匹配。

批处理文件的输出为:

del "C:\xxx\yyy\2004 !Apr_backup_2017_12_18_210001_2986007.bak"
del "C:\xxx\yyy\model%_backup_2017_12_19_210003_2544131.bak"
del "C:\xxx\yyy\Subscribers=_backup_2017_12_19_210003_3012893.bak"

这是预期的结果。将删除每个文件对中较旧的文件。

然后我想获取文件名的一部分可能会更容易,因为没有文件扩展名的文件名的其余部分有33个字符的固定长度。

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "BackupFolder=C:\xxx\yyy"

rem Search for files matching the long wildcard pattern
rem *_backup_????_??_??_??????_???????.bak
rem in backup folder and assign each file name without
rem file extension to environment variable.

rem The last 33 characters are removed from each file name to get the
rem file description part at beginning of each file name. Then define
rem an environment variable of which name starts with FileDesc: and
rem ends with the file description. The value assigned to this
rem environment variable is 1.

for /F "delims=" %%I in ('dir "%BackupFolder%\*_backup_????_??_??_??????_???????.bak" /A-D /B /ON 2^>nul') do (
    set "FileName=%%~nI"
    call set "FileDesc:%%FileName:~0,-33%%=1"
)

rem Run command SET with FileDesc: to output all environment variables
rem starting with that string in name and sorted by name and process
rem this list whereby each line ends with =1 as value 1 is assigned
rem to each of these environment variables.

rem For each unique file description in output list assign the file
rem description with =1 appended to environment variable FileDesc
rem and run subroutine DeleteFiles.

for /F "tokens=2 delims=:" %%I in ('set FileDesc: 2^>nul') do (
    set "FileDesc=%%I"
    call :DeleteFiles
)

rem Restore initial environment on starting this batch file and exit it.
endlocal
goto :EOF


rem The subroutine DeleteFiles removes first from passed file description
rem the last two characters being always =1 from list of environment
rem variables starting with FileDesc: and appends the backup wildcard
rem pattern.

rem Command DIR is used to find all files in backup folder starting
rem with current file description and _backup_ and output the found
rem files sorted by last modification date with newest modified file
rem first and oldest modified file last.

rem The command FOR processing this list skips the first file name
rem output by DIR which means the newest file. All other, older
rem files perhaps also found by DIR are deleted one after the other.

:DeleteFiles
set "FilePattern=%FileDesc:~0,-2%_backup_*.bak"
for /F "skip=1 delims=" %%J in ('dir "%BackupFolder%\%FilePattern%" /A-D /B /O-D /TW') do ECHO del "%BackupFolder%\%%J"
goto :EOF

最后一行中还包含ECHO left to命令del的批处理文件在备份文件夹中的六个文件上产生相同的结果。

我不知道在不知道文件名的部分中可能存在哪些字符的情况下,是否可以对批处理文件进行更优化。我没有考虑可能的进一步优化。

让我们假设唯一的[DESC]字符串列表是众所周知的,并且可以在批处理文件中进行硬编码,例如2004!Apr模型%订阅者=对于我的测试用例中的六个文件:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "BackupFolder=C:\xxx\yyy"
for %%I in ("2004 !Apr" "model%%" "Subscribers=") do for /F "skip=1 delims=" %%J in ('dir "%BackupFolder%\%%~I*_backup_*.bak" /A-D /B /O-D /TW 2^>nul') do del "%BackupFolder%\%%J"
endlocal

此批处理文件确实会删除文件,因为最后一行中没有ECHO。

哦,是的,知道各个备份文件名可以让一切变得更简单。

批处理文件甚至可以优化为单个命令行:

@for %%I in ("2004 !Apr" "model%%" "Subscribers=") do @for /F "skip=1 delims=" %%J in ('dir "C:\xxx\yyy\%%~I*_backup_*.bak" /A-D /B /O-D /TW 2^>nul') do @del "C:\xxx\yyy\%%J"

在备份存储介质上创建:

  1. 每三个月备份一次文件名为ComputerName_backup_YYYY_MM. tib的整台机器,需要200 GiB,并且备份存储介质上只有最后一次备份就足够了;
  2. 每周六备份文件夹,其中文件不经常更新为文件名Folder_backup_YYYY_MM_DD. zip,在存储介质上需要大约400 MiB,足以恢复过去四周;
  3. 每天备份文件名为Database_backup_YYYY_MM_DD. bak的数据库文件,目前每个备份需要20 MiB,但与数据库文件的典型情况一样,它或多或少地保持不变,并且在哪里可以恢复过去七天的数据条目。

所需的最小存储介质大小为:

(1+1) × 200 GiB + (4+1) × 400 MiB + (7+1) × (20×3) MiB

根据数据库备份的增长速度,1 TiB的存储介质大小实际上足以满足大约未来三年的需要,计算中已经包括了三倍的增长。

最好删除创建每日数据库备份时不再需要的所有备份文件

@echo off
set "BackupFolder=C:\xxx\yyy"
call :DeleteBackups 1 "ComputerName"
call :DeleteBackups 4 "Folder"
call :DeleteBackups 7 "Database"
goto :EOF

:DeleteBackups
for /F "skip=%1 delims=" %%I in ('dir "%BackupFolder%\%~2*_backup_*" /A-D /B /O-D /TW 2^>nul') do del "%BackupFolder%\%%I"
goto :EOF

删除不再需要的备份非常容易,只要考虑正确的策略。

要了解使用的命令及其工作方式,请打开命令提示窗口,在那里执行以下命令,并仔细阅读为每个命令显示的所有帮助页。

  • <代码>呼叫/

另请阅读Microsoft关于使用命令重定向运算符的文章,了解2的解释

 类似资料:
  • 我被困在一个奇怪的情况下,实际上我有一个工作,它在一个单独的文件夹中创建文件,该工作每天都在运行,所以它每天都在该文件夹中创建文件,该文件扩展名为. dat以及它的zip文件。 现在,假设作业今天运行,它将在该文件夹中创建两个文件,第二天我希望前一天的zip文件保留在该文件夹中,但是。dat文件应在删除之前删除。今天的dat文件得到创建,我已经写了代码,但它没有发生,因为我想请建议如何实现这一点

  • 问题内容: 我希望根据该文件是否已经存在来写一个文件,仅在不存在的情况下才写(实际上,我希望继续尝试文件,直到找到一个不存在的文件为止)。 下面的代码显示在其中一个潜在的攻击者可以插入一个符号,作为建议的方式这篇文章中该文件的测试和写入的文件之间。如果代码以足够高的权限运行,则可能会覆盖任意文件。 有什么办法解决这个问题? 问题答案: 编辑 :另请参见DaveJones的回答:从Python3.3

  • 问题内容: 我需要将以下行添加到配置文件的末尾: 到一个名为 我正在寻找用于执行此操作的方法,但无法解决。 如果该行尚不存在,我将如何插入? 问题答案: 保持简单:) grep + echo 应该足够了: 安静 整行匹配 模式是一个普通的字符串 https://linux.die.net/man/1/grep 编辑:合并@cerin和@ thijs-wouters建议 。

  • 问题内容: 我正在使用postgreSQL9.1,并且想使用此提示从表中删除重复项: 另一种可能的方式是 我在ORDER BY (SELECT 0)上面使用,因为在打平的情况下保留哪一行是任意的。 为了保留最新的RowID顺序,例如,您可以使用ORDER BY RowID DESC 执行计划 执行计划通常比接受的答案更简单,更有效,因为它不需要自我连接。 执行计划 但是,情况并非总是如此。一种GR

  • 我有一个非常简单的功能: 该文件在执行后显示不可读,并从文件目录中消失。但是,当从浏览器访问时,它仍然可用,尽管没有缓存(在不同的浏览器上打开文件进行测试)。我是不是错过了什么?文件是否被服务器缓存?这是我能想到的唯一解释。

  • 我想创建一个文件;如果它已经存在,我想删除它并重新创建它。我尝试这样做,但它抛出一个Win32错误。我做错了什么?