Link to home
Start Free TrialLog in
Avatar of Giggz
Giggz

asked on

batch script to delete some files

i'm simply looking to create a batch script that can "delete files 30days old or older" ONLY in specified folders.

i want the script to search the whole drive including unlimited levels of subdirectories for folders named WHATEVER_I_WANT.
there will be hundreds of the same folder name in different directories/subdirectories/subsubdirectories etc...
then the script will delete ONLY the files that are 30+ days that are inside the WHATEVER_I_WANT folders.

i would like to be able to run the script on all versions of windows: xp, vista, 7, 8, 10, server 2003, server 2008, server 2012, soon to be server 2016.
Avatar of NVIT
NVIT
Flag of United States of America image

Make a .bat file of this.
Open a CMD window and run the .bat.
You will see results echoed to screen. Also logged to file.

Note:
- Currently will ECHO qualified files to screen and log file. Remove capitalized ECHO at cmd /c ECHO to run for real.
- Adjust values after SET variables to your needs.
- Deleted filenames are logged to file FNLog

@echo off
set DaysOld=30
set RootDir=C:\
set FNDirs=c:\dirs.txt
set FindDir=WHATEVER_I_WANT
set FNPatt=*.*
set FNLog=c:\utils\DelFiles.txt

dir /b /s /ad "%RootDir%" > "%FNDirs%"
for /f %%a in ('type %FNDirs% ^| find "%FindDir%"') do (
  forfiles /d -%DaysOld% /m "%FNPatt%" /p "%%a" /c "cmd /c ECHO del /q /f @path&echo %date% %time% Deleted @path>>"%FNLog%"
)

Open in new window

Avatar of oBdA
oBdA

One important question: what should happen with files that are in a subfolder of a "WHATEVER_I_WANT" folder, but not direct children (like "C:\Temp\WHATEVER_I_WANT\SomeOtherFolder\old.txt")? Should these be processed, too?
the script below will only process files that are direct children of a folder named WHATEVER_I_WANT (so anything like "C:\Temp\WHATEVER_I_WANT\SomeOtherFolder\old.txt" will not be deleted).

Now, IIRC, forfiles.exe isn't part of Windows XP, and I couldn't find a reliable download source for an XP version, so the following script uses robocopy.exe from the W2k3 Resource Kit (http://www.microsoft.com/en-us/download/details.aspx?id=17657) to determine whether the required file age is reached. Just drop the exe in the script's folder (you can use 7-zip to extract first the exe, then robocopy.exe from the msi if you don't want to install the complete kit). Note that there won't be any "real" copying going on; the /L switch will only list files that qualify.
The script is in test mode and will only display the files it would normally delete. Remove the REM in line 15 to run it for real.

@echo off
setlocal enabledelayedexpansion
set SearchRoot=C:\
set SearchFolder=WHATEVER_I_WANT
set FileMask=*.*
set FileAge=30

pushd %~dp0
echo Searching for folder '%SearchFolder%' in '%SearchRoot%' ... 
for /f "delims=" %%a in ('dir /s /b /a:d "%SearchRoot%" ^| findstr.exe /r /c:"\\%SearchFolder%$"') do (
	echo Processing '%%a' ...
	for /f "delims=" %%d in ('robocopy.exe "%%a" "C:\Dummy" %FileMask% /L /njh /njs /ndl /nc /ns /minage:%FileAge%') do (
		call :TrimWS %%d
		echo 	- '!Trimmed!'
		REM del /f "!Trimmed!!
	)
)
popd
echo Done.
goto :eof

:TrimWS
set Trimmed=%*
goto :eof

Open in new window


NVIT,
your script will not only process files in folders named "WHATEVER_I_WANT", it will process files in all folders that have "WHATEVER_I_WANT" as a substring of their name, so something like "WHATEVER_I_WANT_BUT_DO_NOT_DELETE_ANYTHING_INSIDE" would be processed, too.
Avatar of Giggz

ASKER

hi oBdA,
you're awsome man, thx. sorry for not being fully clear on all the details, here it is:

the folder WHATEVER_I_WANT has no subfolders, only files with different extensions, none executables.
it will be a problem to search folders with the name WHATEVER_I_WANT_BUT_DO_NOT_DELETE_ANYTHING_INSIDE because there may be those folders existing. it would be great if we can specify to only search folders with the specific exact name of WHATEVER_I_WANT and nothing else

as for XP, it's really not that big of a deal, as i will be replacing the whole hardware in the next few weeks/months. so it's not as critical for those machines.

thx
Well, my script above will only process folders with the exact name of "WHATEVER_I_WANT".
If you don't need to run the script on XP/W2k3, you can just ignore the part about putting robocopy.exe into the script's folder - Vista/W2k8 and later finally have robocopy.exe as part of the OS.
Here is a small VBS solution that doesn't need any additional files to support it, see how it goes for you.

If you want to run it just for texting, comment out the DELETE line.

strBaseDir = "c:\"
strDirName = "WHATEVER_I_WANT"
intDaysToKeep = 30

' Create file system object
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")

' Remove old files in specified folder
RemoveOldFiles objFSO.GetFolder(strBaseDir)

Sub RemoveOldFiles(objFolder)
    On Error Resume Next

    ' Does this match the name we are looking for, if so process it
    If LCase(objFolder.Name) = LCase(strDirName) Then

       ' Remove any files older than specified days from this folder
       For Each objFile In objFolder.Files
           If DateDiff("d", objFile.DateLastModified, Now) > intDaysToKeep Then
                Wscript.Echo "Deleted file: " & objFile.Path
                objFile.Delete
           End If
       Next

    End If

    ' See if we were able to access this folder, if not don't recurse into it
    If Err.Number = 0 Then
       ' Check subfolders
       For Each objSubFolder In objFolder.Subfolders
           RemoveOldFiles objSubFolder
       Next
    End If
End Sub

Open in new window

~bp
Avatar of Giggz

ASKER

oBdA> thanks for the fast reply,
is it possible to change the script to make it look specifically for that exact match of foldername? instead of looking for all folder names that have that name. example if i call my folder name test-folder:
find "test-folder" = valid
skip "my-test-folder" & "test-folder4"

the folder name will never change, and it's specific that it almost never matches another foldername, but can't guarantee that it won't have xyz-foldername or foldername-xyz.

Bill> thank you for the nice script.
which line number should i comment out? line 20 or line 21?
sorry i don't know much about scripts.

thx
Line 21 should be commented, just insert a single quote before the existing words to make that line a comment.

~bp
Avatar of Giggz

ASKER

it worked pretty good.
now the next question, how can i have it do it's thing unattended without popping up asking me to confirm each file.
as i'm going to run it on remote computers on a windows schedule. so no one will be available to click anything.

i don't even need it to show the removed list on the screen.

i don't mind having some kind of log of the files that are removed, but limit the log to 100 lines. u think that's possible?

thx
If you are talking about my VBS solution, then just comment out the Wscript.Echo on line 20, and undo the commenting of line 21 to let the Delete occur.  It should run silent with that change.

We could add some kind of logging if wanted, and what I'm hearing is you would want to just log the first 100 deletions, and no more?  Where would you want these logged?

~bp
Avatar of Giggz

ASKER

thx Bill, yes i was talking about your script.

this scrip will run daily on the computer, the most deletions it will have in a day would be 2-20 files, it usually will never have more, thats why i suggest having to log the last 100 deletions would be more than sufficient in this case.

make the log in C:\del-log\deleted_files.txt
if the del-log folder doesn't exist, create it; if the deleted_files.txt file doesn't exist, create it.

thank you so much for all your help
As I said: my script above will only process folders with the exact name of "WHATEVER_I_WANT".
You can just run it as it is, it will only display the folders and files it would process, so you can test whatever folder names you want. It will only start deleting once you remove the REM in line 15.
If you are interested in the details: the findstr.exe /r /c:"\\%SearchFolder%$" in line 10 will take care of it. It searches for the folder name with a regular expression, and will only find the folder name if it is enclosed between a backslash (which needs to be escaped in a RE, so it's there twice), and the end of the line (which is a $ in RE).
Writing that, I just noticed that the exact might be too exact - it includes the case and might skip some folders it should delete (but won't delete too much). This one is now case insensitive:
@echo off
setlocal enabledelayedexpansion
set SearchRoot=C:\
set SearchFolder=WHATEVER_I_WANT
set FileMask=*.*
set FileAge=30

pushd %~dp0
echo Searching for folder '%SearchFolder%' in '%SearchRoot%' ... 
for /f "delims=" %%a in ('dir /s /b /a:d "%SearchRoot%" ^| findstr.exe /i /r /c:"\\%SearchFolder%$"') do (
	echo Processing '%%a' ...
	for /f "delims=" %%d in ('robocopy.exe "%%a" "C:\Dummy" %FileMask% /L /njh /njs /ndl /nc /ns /minage:%FileAge%') do (
		call :TrimWS %%d
		echo 	- '!Trimmed!'
		REM del /f "!Trimmed!!
	)
)
popd
echo Done.
goto :eof

:TrimWS
set Trimmed=%*
goto :eof

Open in new window

And for the sake of completeness, here's a version that uses findstr.exe (and so won't work on XP/W2k3; should work on Vista/W2k8 and later). It's in test mode as well and will only report the files it would delete. Remove the uppercase ECHO in line 11 to run it for real.
@echo off
setlocal enabledelayedexpansion
set SearchRoot=C:\
set SearchFolder=WHATEVER_I_WANT
set FileMask=*.*
set FileAge=30

echo Searching for folder '%SearchFolder%' in '%SearchRoot%' ... 
for /f "delims=" %%a in ('dir /s /b /a:d "%SearchRoot%" ^| findstr.exe /i /r /c:"\\%SearchFolder%$"') do (
	echo Processing '%%a' ...
	forfiles.exe /p "%%a" /m %FileMask% /d -%FileAge% /c "cmd.exe /c ECHO del /f @path"
)
echo Done.

Open in new window

Okay, this should handle the logging, and roll of old log records keeping the last 100 (configurable near the top of the code).

' Text file I/O constants
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8

strBaseDir = "c:\"
strDirName = "WHATEVER_I_WANT"
intDaysToKeep = 30
strLogFile = "C:\del-log\deleted_files.txt"
intLogSize = 100

' Create file system object
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")

' Initialize log info for this run
strLogData = ""

' Remove old files in specified folder
RemoveOldFiles objFSO.GetFolder(strBaseDir)

' Truncate and write log file
WriteLogFile strLogFile, strLogData


Sub RemoveOldFiles(objFolder)
    On Error Resume Next

    ' Does this match the name we are looking for, if so process it
    If LCase(objFolder.Name) = LCase(strDirName) Then

       ' Remove any files older than specified days from this folder
       For Each objFile In objFolder.Files
           If DateDiff("d", objFile.DateLastModified, Now) > intDaysToKeep Then
               strLogData = strLogData & Now & " - Deleted file: " & objFile.Path & vbCrLf
               ' objFile.Delete
           End If
       Next

    End If

    ' See if we were able to access this folder, if not don't recurse into it
    If Err.Number = 0 Then
       ' Check subfolders
       For Each objSubFolder In objFolder.Subfolders
           RemoveOldFiles objSubFolder
       Next
    End If
End Sub

Sub WriteLogFile(strFile, strData)
   ' Get any log records from prior run
   If objFSO.FileExists(strFile) Then
      Set objFile = objFSO.OpenTextFile(strFile, ForReading)
      strKeep = objFile.ReadAll
      objFile.Close
   Else
      strKeep = ""
   End If

   ' Add new log records from this run on
   strKeep = strKeep & strData

   ' Only keep the last intLogSize lines in the log (roll off older lines)
   arrKeep = Split(strKeep, vbCrLf)
   If UBound(arrKeep) > intLogSize Then
      strKeep = ""
      For i = (UBound(arrKeep) - intLogSize) To UBound(arrKeep)
         If arrKeep(i) <> "" Then
            strKeep = strKeep & arrKeep(i) & vbCrLf
         End If
      Next
   End If

   ' Write log file data to log file
   Set objFile = objFSO.OpenTextFile(strFile, ForWriting, True)
   objFile.Write strKeep
   objFile.Close
End Sub

Open in new window

~bp
Avatar of Giggz

ASKER

i didn't test it yet, sorry for the delay, i hadn't been feeling ok to do some work.

again i thank you Bill for helping out on this.

so that final reply with the long script you provided, does it run silent and write the logs of upto 100 deletions?

2 more questions, as i think these are the last things i may need with this script. i have 1 customer who may be getting sub-folders inside that specific folder WHATEVER_I_WANT, would the script also delete the subfolders recursively? i'd like it to do so if there were folders older than 30days old.

last thing, can we make it it send the log file by email if i provide an smtp account info?
like: smtp mail server, username, password, port number, no SSL or any security required, also to provide the subject that it would have, and the "from" email address, and the "to" email address.

thank you again
Yes, please test what we have before additional changes can be made.

Yes, should run 'silent' and write to log last 100 records.

Yes, it processes subfolders, please test to make sure it meets your needs.

Yes, it can send an email.  Once you have tested the current version and confirmed everything else is in order we can add that easy enough.

~bp
Avatar of Giggz

ASKER

ok, i will put the script in place, it will run tonight, i will get back to you with updates. thank you again
Avatar of Giggz

ASKER

ok i tried to run the script but it gave me an error, you can see attached image

User generated image
What did you change line 9 to?  Did you perhaps reference a folder that doesn't exist, the script won't automatically create that, only the actual text file for the log.

~bp
Avatar of Giggz

ASKER

oh, i didn't change line 9 at all, i left it as is. sorry i assumed it will auto-create the folder because i mentioned it earlier. but np, i will create the folder manually tonight, and run the script again and see what happens.

ill post back

thx
Avatar of Giggz

ASKER

same error message, i created the folder manually.
i also created a blank txt file with the same name, and i still got the same error msg again.
Can you post up the script you are actually running please?

In addition, at a DOS prompt could you do the following command (assuming the same file name is on line 9, adjust the folder name here if needed) and post up the output:

DIR C:\del-log

~bp
Avatar of Giggz

ASKER

ok here is the exact vbs script i have without any modifications:

' Text file I/O constants
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8

strBaseDir = "E:\User Backups\"
strDirName = "DeletedFiles"
intDaysToKeep = 30
strLogFile = "C:\del-log\deleted_files.txt"
intLogSize = 100

' Create file system object
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")

' Initialize log info for this run
strLogData = ""

' Remove old files in specified folder
RemoveOldFiles objFSO.GetFolder(strBaseDir)

' Truncate and write log file
WriteLogFile strLogFile, strLogData


Sub RemoveOldFiles(objFolder)
    On Error Resume Next

    ' Does this match the name we are looking for, if so process it
    If LCase(objFolder.Name) = LCase(strDirName) Then

       ' Remove any files older than specified days from this folder
       For Each objFile In objFolder.Files
           If DateDiff("d", objFile.DateLastModified, Now) > intDaysToKeep Then
               strLogData = strLogData & Now & " - Deleted file: " & objFile.Path & vbCrLf
               ' objFile.Delete
           End If
       Next

    End If

    ' See if we were able to access this folder, if not don't recurse into it
    If Err.Number = 0 Then
       ' Check subfolders
       For Each objSubFolder In objFolder.Subfolders
           RemoveOldFiles objSubFolder
       Next
    End If
End Sub

Sub WriteLogFile(strFile, strData)
   ' Get any log records from prior run
   If objFSO.FileExists(strFile) Then
      Set objFile = objFSO.OpenTextFile(strFile, ForReading)
      strKeep = objFile.ReadAll
      objFile.Close
   Else
      strKeep = ""
   End If

   ' Add new log records from this run on
   strKeep = strKeep & strData

   ' Only keep the last intLogSize lines in the log (roll off older lines)
   arrKeep = Split(strKeep, vbCrLf)
   If UBound(arrKeep) > intLogSize Then
      strKeep = ""
      For i = (UBound(arrKeep) - intLogSize) To UBound(arrKeep)
         If arrKeep(i) <> "" Then
            strKeep = strKeep & arrKeep(i) & vbCrLf
         End If
      Next
   End If

   ' Write log file data to log file
   Set objFile = objFSO.OpenTextFile(strFile, ForWriting, True)
   objFile.Write strKeep
   objFile.Close
End Sub

                                          

Open in new window

And the DIR output?

~bp
Avatar of Giggz

ASKER

here you go

C:\>DIR C:\del-log
 Volume in drive C has no label.
 Volume Serial Number is 7A8A-665E

 Directory of C:\del-log

10/15/2015  08:46 PM    <DIR>          .
10/15/2015  08:46 PM    <DIR>          ..
10/15/2015  08:45 PM                 0 deleted_files.txt
               1 File(s)              0 bytes
               2 Dir(s)  528,866,246,656 bytes free

C:\>

Open in new window

The only way I could reproduce that error was when the c:\del-log folder didn't exist.

I have added a couple of edits near the start of the processing to make sure the base directory, and the parent directory of the log file exist.  If they don't it will now produce an error message.

In addition I removed the trailing backslash from the strBaseDir folder name, that should not be there.

Try this and see what it says.  If you still get the same error then try a different folder for the log file.

' Text file I/O constants
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8

strBaseDir = "E:\User Backups"
strDirName = "DeletedFiles"
intDaysToKeep = 30
strLogFile = "C:\del-log\deleted_files.txt"
intLogSize = 100

' Create file system object
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")

' Do some basic editing on folder names specified
If Not ObjFSO.FolderExists(strBaseDir) Then
   Wscript.Echo "*ERROR* Folder to scan does not exist: """ & strBaseDir & """."
   Wscript.Quit
End If
If Not ObjFSO.FolderExists(objFSO.GetParentFolderName(strLogFile)) Then
   Wscript.Echo "*ERROR* Folder for log file does not exist: """ & objFSO.GetParentFolderName(strLogFile) & """."
   Wscript.Quit
End If

' Initialize log info for this run
strLogData = ""

' Remove old files in specified folder
RemoveOldFiles objFSO.GetFolder(strBaseDir)

' Truncate and write log file
WriteLogFile strLogFile, strLogData


Sub RemoveOldFiles(objFolder)
    On Error Resume Next

    ' Does this match the name we are looking for, if so process it
    If LCase(objFolder.Name) = LCase(strDirName) Then

       ' Remove any files older than specified days from this folder
       For Each objFile In objFolder.Files
           If DateDiff("d", objFile.DateLastModified, Now) > intDaysToKeep Then
               strLogData = strLogData & Now & " - Deleted file: " & objFile.Path & vbCrLf
               ' objFile.Delete
           End If
       Next

    End If

    ' See if we were able to access this folder, if not don't recurse into it
    If Err.Number = 0 Then
       ' Check subfolders
       For Each objSubFolder In objFolder.Subfolders
           RemoveOldFiles objSubFolder
       Next
    End If
End Sub

Sub WriteLogFile(strFile, strData)
   ' Get any log records from prior run
   If objFSO.FileExists(strFile) Then
      Set objFile = objFSO.OpenTextFile(strFile, ForReading)
      If objFile.AtEndOfStream Then
         strKeep = ""
      Else
         strKeep = objFile.ReadAll
      End If
      objFile.Close
   Else
      strKeep = ""
   End If

   ' Add new log records from this run on
   strKeep = strKeep & strData

   ' Only keep the last intLogSize lines in the log (roll off older lines)
   arrKeep = Split(strKeep, vbCrLf)
   If UBound(arrKeep) > intLogSize Then
      strKeep = ""
      For i = (UBound(arrKeep) - intLogSize) To UBound(arrKeep)
         If arrKeep(i) <> "" Then
            strKeep = strKeep & arrKeep(i) & vbCrLf
         End If
      Next
   End If

   ' Write log file data to log file
   Set objFile = objFSO.OpenTextFile(strFile, ForWriting, True)
   objFile.Write strKeep
   objFile.Close
End Sub

Open in new window

~bp
Avatar of Giggz

ASKER

great thx, i will test it tonight, as the machine is being used at this moment, but will be available for me tonight.
Avatar of Giggz

ASKER

i just managed to test it, it worked, no errors, and it saved the log in the same exact file i had created previously.
so it must have been the trailing backslash, because the folder and the file were there without any modifications.

so i uncommented line 45, so i can make it do it's thing for real, so far so good with all files in the "DeletedFiles" folder, and all files inside sub-folders that are in "DeletedFiles", but it doesn't delete the subfolders that are older than 30days. would it be possible to have the script check if the sub-folder empty, and if it is, then delete it if it's older than the specified 30days old.

last thing i guess is having it send the log by email.

thx
ASKER CERTIFIED SOLUTION
Avatar of Bill Prew
Bill Prew

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Giggz

ASKER

please accept my sincere apology for the late reply, I had shortage on staff, and i had to cover for 3 people's jobs.

the script you made is perfect, thank you again for the hard work, it's exactly what i'm looking for.

keep up the great work :)
No problem on delay, completely understand.  Glad that was helpful and  thanks for the feedback.

~bp