Reputation: 43
I am writing a batch script to backup files and want to include logic that deletes the oldest file in the folder if the number of backups ever exceeds a certain number set by a variable. Pretty new to batch and my current code deletes all files once it exceeds the number.
Any ideas how to do this?
SET Count=0
FOR %%A IN ("%SNAPSHOTNAME%*.*") DO SET /A Count += 1
IF %Count% gtr %NumberOfBackups% FOR %%A IN ("%SNAPSHOTNAME%_PBCS_Test_*.*") DO del "%%A"
Upvotes: 3
Views: 5229
Reputation: 49086
I suggest the following single command line for this task:
for /F "skip=%NumberOfBackups% eol=| delims=" %%I in ('dir "%SNAPSHOTNAME%_PBCS_Test_*.*" /A-D-H /B /O-D 2^>nul') do del "%%I"
FOR executes in a separate command process started with cmd.exe /C
in background the command line:
dir "%SNAPSHOTNAME%_PBCS_Test_*.*" /A-D-H /B /O-D 2>nul
DIR outputs to handle STDOUT line by line
/A-D-H
(not attribute directory or hidden)/B
which means file name onlyAn error message output by DIR to handle STDERR is suppressed by redirecting it to device NUL with 2>nul
.
Read also the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul
. The redirection operator >
must be escaped with caret character ^
on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir
command line with using a separate command process started in background.
FOR captures this output and processes it line by line according to the options specified in the double quoted string with ignoring empty lines which do not exist in this output of DIR.
The first %NumberOfBackups%
lines are skipped by FOR which means the newest %NumberOfBackups%
are always kept in current directory.
FOR by default ignores also lines starting with a semicolon because of ;
is the default for end of line character. For that reason eol=|
is used to change end of line character to vertical bar which is not possible in a file or folder name and so it is impossible that a file name output by DIR starts with |
.
FOR by default splits a line into substrings using normal space and horizontal tab character as string delimiters and assigns just first space/tab separated string to loop variable I
. This string splitting behavior can be disabled by specifying an empty list of delimiters with delims=
at end of the options string to get the entire line (= file name) assigned to the loop variable I
.
So DIR outputs the names all non-hidden files matching pattern "%SNAPSHOTNAME%_PBCS_Test_*.*"
line by line with newest file first and oldest file last and FOR ignores the first/newest %NumberOfBackups%
files and deletes all other older files.
FOR does nothing if DIR outputs less or exactly %NumberOfBackups%
file names.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
del /?
dir /?
for /?
Note: This is nearly the same answer as written by Squashman, but with full explanation how it works.
Upvotes: 5
Reputation: 14290
If you change to using a FOR /F command with the SKIP option, you can get rid of the IF comparison.
set "NumberOfBackups=6"
FOR /F "SKIP=%NumberOfBackups% DELIMS=" %%G IN ('DIR /A-D /B /O-D "%SNAPSHOTNAME%_PBCS_Test_*.*"') DO DEL "%%G"
Upvotes: 2
Reputation: 66
Your second for loop has no sort and no break condition so as mentioned by you it will delete all files.
A dir
command can sort the files so instead of taking the for
over all files take a for
over the sorted output of an dir
command. Option /OD
will sort for file timestamp.
To let for loop handle the output use for /f
. The break condition is to only delete the first (in this case the oldest) file and than jump out the for loop.
IF %Count% gtr %NumberOfBackups% for /f "delims=" %%f in ('dir /B /OD "%SNAPSHOTNAME%_PBCS_Test_*.*"') do del "%%f" & goto :gotIt
:gotIt
Upvotes: 1