为什么文件存在和文件大小检查不能结合使用?

Why do file exist and file size checks not work in combination?

我尝试编写的批处理文件有问题。

目标: 检查 logfile.log 是否存在,如果不存在则创建它。否则,如果文件已经存在,则应检查文件大小。如果日志文件大小低于一定数量 "do nothing",否则复制日志并用新名称保存并重置实际日志文件的内容。

问题: 由于某些原因,当我使用 exist 检查 size检查。

两个分开的操作都没有问题。如果我删除 exist 条件并保留其余代码并且 logfile.log 已经存在,则一切正常。如果大小太大并且实际文件按预期重置,则会创建带有时间戳的日志文件。

set LOGFILE=%batdir%logfile.log
set maxbytesize=4000

if exist %LOGFILE% (
    for /F "usebackq" %%A IN ('%LOGFILE%') DO set size=%%~zA
    if %size% GTR %maxbytesize% (
        copy %LOGFILE% %LOGFILE%_%timestamp%
        :: overwrite existing file with ">"
        >%LOGFILE% echo Timestamp: %timestamplog%
        >>%LOGFILE% echo old log: %LOGFILE%_%timestamp%
    )
) else (
    echo Do Nothing >>%LOGFILE%
)

这个问题是由经典的 delayed expansion 陷阱引起的,每个批处理文件编写的初学者都会陷入从未阅读命令 SET 输出的帮助 运行 在命令提示符下 window set /?.

(开始并以匹配)结束的命令块由Windows命令处理器解析执行命令之前使用这个命令方块。在命令块解析期间,所有出现的 %variable% 都将替换为引用环境变量的当前值。这可以在 .
上看到 另请参阅:How does the Windows Command Interpreter (CMD.EXE) parse scripts?

所以在这种情况下 %size% 被替换为空,因为在执行命令 IF 之前,这个环境变量很可能在执行批处理文件时不存在这是检查日志文件的存在。真实存在的日志文件命令块中的环境变量定义size不会生效,因为%size%在命令块解析阶段已被空字符串替换。

在这种情况下最有可能的最佳解决方案是避开所有命令方块。那么延迟环境变量扩展也不需要。

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LogFile=%~dpn0.log"
set "MaxLogFileSize=4000"

if not exist "%LogFile%" echo Do Nothing>>"%LogFile%"& goto DoMore

for %%I in ("%LogFile%") do set "LogFileSize=%%~zI"
if %LogFileSize% LSS %MaxLogFileSize% goto DoMore

for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "TimeStamp=%%I"
set "FileTimeStamp=%TimeStamp:~0,4%-%TimeStamp:~4,2%-%TimeStamp:~6,2%_%TimeStamp:~8,2%-%TimeStamp:~10,2%-%TimeStamp:~12,2%"
set "TextTimeStamp=%TimeStamp:~0,4%-%TimeStamp:~4,2%-%TimeStamp:~6,2% %TimeStamp:~8,2%:%TimeStamp:~10,2%:%TimeStamp:~12,2%"

move /Y "%LogFile%" "%~dpn0_%FileTimeStamp%.log"
>"%LogFile%" echo Time stamp: %TextTimeStamp%
>>"%LogFile%" echo Old log: %~n0_%FileTimeStamp%.log

:DoMore
rem Add here more command lines using "%LogFile%".
endlocal

在我看来,限制日志文件的大小并使用包含当前本地日期和时间的文件名另外保持日志文件超过限制不是一个好主意。这导致存储介质上的日志文件越来越多,日志文件的文件大小限制不会阻止使用越来越多的存储空间space。

最好简单地移动已经过大的具有固定文件名的日志文件,覆盖具有该文件名的现有文件。

示例:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LogFile=%~dpn0.log"
set "MaxLogFileSize=4000"
set "OldLogFile=%~dpn0_old.log"

if exist "%LogFile%" for %%I in ("%LogFile%") do if %%~zI GTR %MaxLogFileSize% move /Y "%LogFile%" "%OldLogFile%"

rem Add here more command lines using "%LogFile%".
endlocal

批处理文件在超过日志文件限制时用当前日志文件覆盖 *_old.log 文件。因此,当前日志文件包含有关最后操作的记录信息,文件 *_old.log 包含一段时间内记录的存档日志信息,具体取决于每次执行批处理文件时记录的信息量以及批处理文件的频率在一定的时间跨度内执行。曾经需要的最大存储量 space 大约是 MaxLogFileSize 值的两倍,并且没有人必须定期删除以后再也没有人查看过的旧日志文件。

注: Windows 命令处理器使用 32 位有符号整数算法。因此 MaxLogFileSize 的值必须比最大正 32 位有符号整数低足够的字节,即 2 GiB 或 2147483647 字节,以防止当前日志文件变得更大或等于 2147483648 字节。

要了解使用的命令及其工作原理,请打开命令提示符 window,在其中执行以下命令,并仔细阅读为每个命令显示的所有帮助页面。

  • call /? ... 解释 %~dpn0 扩展为不带文件扩展名的批处理文件的驱动器、路径和名称。
  • echo /?
  • endlocal /?
  • for /?
  • goto /?
  • if /?
  • move /?
  • rem /?
  • set /?
  • setlocal /?
  • wmic /?
  • wmic os /?
  • wmic os get /?
  • wmic os get localdatetime /?

我也推荐阅读:


  • 它解释了用于获取格式为 yyyy-MM-dd_HH-mm-ss 的文件名和格式为 yyyy-MM-dd HH:mm:ss.
  • 的日志文件内容的时间戳的代码
  • Why is no string output with 'echo %var%' after using 'set var = text' on command line?