7

I'm writing a simple .bat backup script, and as part of it I want the oldest backup (folder) to be deleted upon reaching a set max limit of backups.

Right now I have this:

%COUNTER% is based on the number of backup folders currently in the directory where backups are stored, and is calculated earlier in the script.

%MAXBACKUPS% is just a user-specified number like "10," to say that you only want to keep up to 10 versions of the backups.

:: Delete the oldest backup, but only if we've exceeded the desired number of backups.
IF %COUNTER% gtr %MAXBACKUPS% (
ECHO More than %MAXBACKUPS% backups exist. Deleting oldest...
FOR /f "delims=" %%a in ('dir "..\Backup\*" /t:c /a:d /o:-d /b') do rd /s /q "..\Backup\%%a"
::Increment the counter down since we've just removed a backup folder.
SET /a COUNTER=%COUNTER%-1
)

I would like this script to only delete the one oldest folder in the ..\Backup folder, but as it stands it seems to delete every single folder it finds once it reaches the backup limit, which is obviously not the desired behavior.

Ectropy
  • 1,533
  • 4
  • 20
  • 37
  • well, the script deletes every single folder because you have a loop in there which calls `rd` for each folder. That is what the `for` command does. Why did you write it that way? – HugoRune Feb 11 '15 at 23:36
  • I thought I needed for to correctly sort all the folders by date, but now that I think about it, that's probably not the case. – Ectropy Feb 11 '15 at 23:38
  • As it turns out, it is the case, but the way I did it originally was a bit off. – Ectropy Feb 12 '15 at 03:16

4 Answers4

5

You were so close ! :-)

All you need to do is skip the first %MAXBACKUPS% entries when sorted by date descending. You don't even need your COUNTER variable.

:: Preserve only the %MAXBACKUPS% most recent backups.
set "delMsg="
for /f "skip=%MAXBACKUPS% delims=" %%a in (
  'dir "..\Backup\*" /t:c /a:d /o:-d /b'
) do (
  if not defined delMsg (
    set delMsg=1
    echo More than %MAXBACKUPS% found - only the %MAXBACKUPS% most recent folders will be preserved.
  )
  rd /s /q "..\Backup\%%a"
)
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • I think this is the best way to accomplish it, especially since it doesn't require a counter, and can delete extra backups if you lower the backup limit at a later date. I made a slight modification for my implementation which I will show in a separate answer for those interested. – Ectropy Feb 12 '15 at 03:10
  • Can you explain the code a bit more? I am trying to accomplish the same but I cannot seem to get to work. I am backing up a SQL database every night and I want to delete the 5th file. My folder structure is E:\backups\NC\ bunch of files. NC_1, NC_2 etc.. I am trying to keep the latest 4 files. – Victor_Tlepshev Nov 25 '19 at 14:48
  • @Victor_Tlepshev - This answer is designed to satisfy the OP's requirement to remove all but the N most recent ***folders***. It should not be hard to modify to delete files instead by changing `dir` to use `/a:-d` and change `rd /s /q` to `del`. Post a new question if you continue to have trouble. – dbenham Nov 25 '19 at 16:39
3

A simple way to do this based on your script:

FOR /f "delims=" %%a in ('dir "..\Backup\*" /t:c /a:d /o:-d /b') do set lastfolder=%%a

rd /s /q "..\Backup\%lastfolder%"

So you still loop over each folder, sorted by age, but you overwrite %lastfolder% so at the end it contains only the name of the oldest folder.

Then you delete that folder.

HugoRune
  • 13,157
  • 7
  • 69
  • 144
  • I placed `ECHO %LASTFOLDER%` after the FOR line you suggested, but it reports `ECHO is off.` as though there is nothing stored in that variable by the end of the for loop. Can you maybe not access variables set inside of for loops? If you can, there is some other issue in play. – Ectropy Feb 11 '15 at 23:58
  • it works on my system. If in doubt, place `echo` after the `do` ( or just remove any `echo off` at the start of the script), that way you can see which `set` is the last one to execute. Batch does not have local scopes for variables, all variables inside the loop are accessible from anywhere. – HugoRune Feb 12 '15 at 00:18
3

Here's the final block of code I ended up using:

:: Preserve only the %MAXBACKUPS% most recent backups.
FOR /f "skip=%MAXBACKUPS% delims=" %%a in (
'dir "..\Backup\*" /t:c /a:d /o:-d /b'
) do (
ECHO More than %MAXBACKUPS% backups found--Deleting "%%a".
ECHO.
rd /s /q "..\Backup\%%a"
)

It simplifies the deletion message code a bit, and provides the end user with info about what file was deleted in the command prompt window.

Based on dbenham's answer.

Community
  • 1
  • 1
Ectropy
  • 1,533
  • 4
  • 20
  • 37
2
FOR /f "delims=" %%a in ('dir "..\Backup\*" /t:c /a:d /o:d /b') do (
 rd /s /q "..\Backup\%%a"
 goto :break
)
:break

you can break the for loop with goto

npocmaka
  • 55,367
  • 18
  • 148
  • 187