如何使我的批处理脚本删除旧文件有效?

时间:2009-11-23 00:08:18

标签: windows file batch-file scripting

在文件夹内(例如c:\ test)如果文件数超过21,我想删除最旧的文件。

这是我想出的,问题是它一次删除最旧的文件,第二次运行它什么也没做,删除最旧的第三个,继续这样。它也不关心文件数量是否低于21,即使它更低也会删除。

以下是代码:

SET BACKUPDIR=C:\test
SET countfiles=dir BACKUPDIR /b | find /v /c "::"

if countfiles GTR 21 (
FOR /F %%i IN ('DIR /B /O-D %BACKUPDIR%') DO SET OLDEST=%%i
DEL %BACKUPDIR%\%OLDEST%
)

2 个答案:

答案 0 :(得分:4)

这有很多问题。

  1. 环境变量与文字字符串:

    SET countfiles=dir BACKUPDIR /b | find /v /c "::"
    

    在这里,您尝试列出名为“BACKUPDIR”的目录中的文件,而不是使用环境变量。它应该是

    SET countfiles=dir %BACKUPDIR% /b | find /v /c "::"
    
  2. 您尝试获取文件数:

    SET countfiles=dir %BACKUPDIR% /b | find /v /c "::"
    

    不可能奏效。这只是将字符串'dir BACKUPDIR /b | find /v /c "::" '分配给环境变量countfiles。它不会运行任何东西 - cmd应该如何知道这是一个执行命令?

    您想要的是以下内容:

    for /f %%x in ('dir %BACKUPDIR% /b ^| find /v /c "::"') do set countfiles=%%x
    

    这会将countfiles环境变量的内容设置为上述命令的输出。您可以使用help for找到更多相关信息。

  3. 您的比较:

    if countfiles GTR 21
    

    比较字符串 "countfiles"和字符串"21"。由于countfiles21之后排序,因此这种情况将始终为真。

    您想在此处使用环境变量%countfiles%

    if %countfiles% GTR 21
    
  4. 您在“块”中尝试“正常”变量扩展:

    if %countfiles% GTR 21 (
        FOR /F %%i IN ('DIR /B /O-D %BACKUPDIR%') DO SET OLDEST=%%i
        DEL %BACKUPDIR%\%OLDEST%
    )
    

    由于cmd在解析命令时会立即扩展环境变量,因此会导致问题。请注意,上面的块计为cmd的单个命令。完整的块将在运行之前进行解析,并且内部的所有变量在运行时都已被替换,因此这不是处理可能会更改内部块的变量的正确方法。

    此处的解决方案是延迟扩展。您可以在批次开头包含以下行:

    setlocal enabledelayedexpansion
    

    而不是使用像%OLDEST%这样的变量,而是使用!OLDEST!!也扩展变量,但是在执行之前直接执行,而不是在解析时执行。所以你的代码片段会变成:

    if %countfiles% GTR 21 (
        FOR /F %%i IN ('DIR /B /O-D %BACKUPDIR%') DO SET OLDEST=%%i
        DEL %BACKUPDIR%\!OLDEST!
    )
    
  5. 如果您希望文件名包含空格,则最后一个块中的for /f循环无法按预期工作。您可以像这样修改它:

    for /f "delims=" %%i in ...
    

    这可以确保该行不会在空格处被破坏(您只需要第一个令牌 - 文件名中的第一个空格 - 而不是完整的文件名。

  6. 总而言之,您的程序可能如下所示:

    SET BACKUPDIR=C:\test
    for /f %%x in ('dir %BACKUPDIR% /b ^| find /v /c "::"') do set countfiles=%%x
    
    if %countfiles% GTR 21 (
        FOR /F "delims=" %%i IN ('DIR /B /O-D %BACKUPDIR%') DO SET OLDEST=%%i
        DEL %BACKUPDIR%\!OLDEST!
    )
    

    并且应该大致工作。我没有测试它的每个方面(床上电话),但我相信它比以前好多了,我希望你能理解上面的修复以及为什么它们是必要的。

    顺便说一下:find /v /c "::"是一个漂亮的小技巧。不知道那一个还是想到它。

    以防万一:如果你真的想继续删除最旧的文件,直到你的文件数量低于21,那么那个单if将不会删除它。您需要设置循环,方法是继续迭代并重新计算每次迭代,或者只计算要删除的文件数。不过,我会把它留作练习。 (提示:循环可以使用标签和条件goto或使用for /l来完成。后者需要更多延迟扩展,所以要小心。)

答案 1 :(得分:1)

这是vbscript的另一种选择,它只迭代目录一次。

Set objFS=CreateObject("Scripting.FileSystemObject")
Set objArgs = WScript.Arguments
strFolder = objArgs(0)
strScriptName = WScript.ScriptFullName 'get script name
Set objFolder = objFS.GetFolder(strFolder)
Set dict = CreateObject("Scripting.Dictionary") 
current  = Now
temp=0
count=0 'count files
For Each strFile In objFolder.Files
    If strFile.Path <> strScriptName Then 'do if not script name
      count=count+1
      dict.Add strFile.Path, strFile.DateLastModified
      'if there are more than 21 files
      If count >= 21 Then
        flag=1
      End If
    End If
Next

filedate = dict.Items
filename = dict.keys
If flag = 1 Then
    For i = 0 To dict.Count -1 'iterate dictionary
      diffdate = DateDiff("d",filedate(i),now)
      If num_days_diff >= temp Then
        oldestfile = filename(i)
        oldestdate = filedate(i)
        temp = num_days_diff
      End If             
    Next
End If 
Set dict=Nothing
WScript.Echo "Oldest file: " & oldestfile, oldestdate
objFS.DeleteFile(oldestfile)

另存为deletefile.vbs并在命令行中

c:\test> cscript /nologo deletefile.vbs c:\path\somewhere