我认为,一份未知的名单[DESC]
所有备份文件名中的字符串在批处理文件中最难处理。代码可以非常简单地了解这个列表,如下所示,或者至少了解这些字符串是否不包含对批处理文件处理至关重要的字符,例如!%=
.
但是未知列表的编码挑战[DESCR]
文件名中带有特殊字符的字符串对我来说很有趣,因此我首先开发了以下注释批处理文件:
@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
命令ECHO在命令之前的最后一行del
结果只是显示哪些文件将被删除,而不是真正删除它们。
选项skip=1
最后一行确定始终保留多少个备份文件。
例如使用skip=5
结果是根据备份文件上的最后修改日期(通常也是创建日期)保留最新的五个文件,并删除所有其他文件。
这样的备份删除策略的优点是无所谓:
- 创建特定备份的频率 - 每天、每周或每月;
- 上次备份创建是否成功;
- 是否手动删除了部分甚至全部备份文件;
- 每个单独的备份文件有多旧;
- 执行删除备份文件的批处理文件的频率。
删除备份真正重要的是每个备份所需的存储大小以及删除过程后剩余多少可用存储空间。备份文件的文件日期不限制可用存储大小。所有剩余备份文件的文件大小和备份介质上的总存储大小是真正重要的因素。这就是为什么我不明白所有这些“删除早于”的问题。只要有足够的可用空间容纳新文件,谁还需要关心文件的年龄?
文件创建日期也可以通过使用/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"
这就是预期的结果。每个文件对中较旧的文件将被删除。
然后我想得到[DESC]
文件名的一部分可以更容易完成,因为不带文件扩展名的文件名的其余部分的固定长度为 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留给指挥del
最后一行对备份文件夹中的六个文件产生相同的结果。
我不知道批处理文件是否可以在不知道其中可能存在哪些字符的情况下进一步优化[DESC]
文件名的一部分。我没有考虑可能的进一步优化。
让我们假设唯一的列表[DESC]
字符串是众所周知的,可以在批处理文件中进行硬编码,例如2004 !Apr
, model%
and Subscribers=
对于我的测试用例中的六个文件:
@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"
最佳和简单的备份删除概念
在备份存储介质上创建:
- 带有文件名的整个机器的备份
ComputerName_backup_YYYY_MM.tib
每三个月一次,占用 200 GiB 的空间,并且备份存储介质上只需要最后一次备份就足够了;
- 包含不经常更新文件名的文件的文件夹的备份
Folder_backup_YYYY_MM_DD.zip
每周六,存储介质上大约需要 400 MiB,足以恢复过去四个星期;
- 带有文件名的数据库文件的备份
Database_backup_YYYY_MM_DD.bak
每天,目前每个备份需要 20 MiB,但与数据库文件的典型情况一样,其增长或多或少会持续增长,并且应该可以恢复过去 7 天的数据条目。
所需的最小存储介质大小为:
(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
只要考虑正确的策略,删除不再需要的备份就可以非常容易。
为了了解所使用的命令及其工作原理,请打开命令提示符窗口,执行以下命令,并仔细阅读为每个命令显示的所有帮助页面。
call /?
del /?
dir /?
echo /?
endlocal /?
for /?
goto /?
rem /?
set /?
setlocal /?
另请阅读 Microsoft 的文章:使用命令重定向运算符 https://technet.microsoft.com/en-us/library/bb490982.aspx的解释2>nul
。重定向运算符>
必须用插入符号转义^
on FOR当 Windows 命令解释器在执行命令之前处理此命令行时,命令行将被解释为文字字符FOR它执行嵌入的dir
命令行使用在后台启动的单独命令进程。