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

Posted

技术标签:

【中文标题】为啥文件存在和文件大小检查不能结合使用?【英文标题】:Why do file exist and file size checks not work in combination?为什么文件存在和文件大小检查不能结合使用? 【发布时间】:2019-03-03 05:32:52 【问题描述】:

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

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

问题:由于某种原因,当我使用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%
)

【问题讨论】:

【参考方案1】:

此问题是由经典的delayed expansion 陷阱引起的,每个初学者在批处理文件写入中遇到从未阅读过命令提示符窗口中运行时输出的命令 SET 的帮助set /?

( 开头并以匹配的) 结尾的命令块由Windows 命令处理器 执行使用此命令块的命令之前解析。在命令块解析期间,所有出现的 %variable% 都将替换为引用的环境变量的当前值。这可以在debugging a batch file 上看到。 另请参阅: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

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

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

例子:

@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 包含在一段时间内记录的存档记录信息,具体取决于每次执行批处理文件时记录了多少信息以及执行批处理文件的频率在一定的时间跨度内。所需要的最大存储空间大约是MaxLogFileSize 的两倍,并且没有人需要定期删除以后再也不会被任何人查看的旧日志文件。

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

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

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

我也推荐阅读:

Why does %date% produce a different result in batch file executed as scheduled task? 它解释了用于获取格式为 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? Why does ECHO command print some extra trailing space into the file?

【讨论】:

以上是关于为啥文件存在和文件大小检查不能结合使用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥光栅文件大小与对象大小有很大不同?

为啥文件压缩后和原文件大小不一样

腾讯微云上传文件大小限制是多少?为啥我不能上传超过1g的文件?今天刚用不了解,我记得以前不是宣传

为啥 Redshift 和 S3 之间的 AWS 文件大小不同?

“文件夹”-“属性”为啥查看文件大小和占用空间数据是变化的?

为啥同样的文件我压缩后和原来的文件大小差不多?