Friday, September 17, 2010

File System Cleanup


Let's face it both applications and people are terrible at cleaning up after themselves. As a result, cleaning up file systems has to be one of the most common requests I have had to help organizations through in my career and it's not always the servers needing the cleanup. Workstations often need the cleanup far more than the servers do. Organizations often target the network share locations for cleanup because that is where they see direct costs associated with this unnecessary collection of files in the form of additional backup media and premature system replacement costs. Workstations though also have file system cleanup needs as well.
We covered removing aged user profiles yesterday. There is also a need though to clean up the profiles and other folders that are still in use. Items such as old Temp Files and System Mini Dumps can easily consume an entire hard drive.
Luckily, a good start on addressing both the server and workstation cleanup tasks can be done in the same script. This is because both have similar needs. Both tasks need a way of reporting and/or deleting files that match a specified file name pattern. They also both need a way of reporting and/or deleting files over a certain age that match a specified name pattern. Today's included script can actually address one or both problems in a single pass through the file system.
This script, filecleanup.vbs, was put together from a bunch of different sources over the years to address the various cleanup requests I had received.
Using the script is simple assuming you have a good grasp of VBScript's regular expressions. See http://msdn.microsoft.com/en-us/library/ms974570.aspx for a quick refresher if you need help constructing regular expressions.
Since most file cleanup searches tend to be focused on the extension of the file in question, the one operator you will use continually is the $ operator as it means the expression needs to appear at the end. For example, the expression ".tmp$" will only match files with an extension of .tmp; it would not match "name.tmp.txt" The script does allow for multiple expressions to be provided in the same operation. Simply delimit the different strings with a semicolon (;). For example, ".tmp$;.err$;.log$" would match any file with a .tmp, .err, or .log extension.
Command line syntax:
Cscript filecleanup.vbs -FOLDER <folder> [-PATTERN <regexpr>] [-DATEPATTERN <regexpr> -NUMDAYS <n>] [-DELETE]
If you have file types that you want removed regardless of age, simply construct a regular expression that matches their name and include it in the –PATTERN argument to the script. For example, if you had a folder named c:\files that you wanted to remove every .dmp and .tmp file from, the command line would be:
Cscript filecleanup.vbs -FOLDER c:\files -PATTERN .dmp$;.tmp$ -delete
If you have file types that you want removed only after they reach a certain age, simply construct a regular expression that matches their name and include it in the –DATEPATTERN argument to the script along with the number of days old at which you wish to remove them. For example, if you wish to remove all .log and .err files from the c:\files folder after 3 days the command line would be:
Cscript filecleanup.vbs -FOLDER c:\files -DATEPATTERN .log$;.err$ -NUMDAYS 3 -delete
The two previous examples can even be combined into a single action. If that were the case the command line would be:
Cscript filecleanup.vbs -FOLDER c:\files -PATTERN .dmp$;.tmp$ -DATEPATTERN .log$;.err$ -NUMDAYS 3 -delete
If you simply want a list of files that meet the criteria, but don't want to delete them, simply leave off the –delete argument and the script will simply report the names and their location.
What the script does not do is cleanup empty folders.
As one can imagine, this script can still be used for a number of very useful things such as:
  • Cleaning up end user profiles of TMP files over a certain age
  • Cleaning up transient network locations such as scan destinations
  • Removing unwanted file types from network shares such as potentially .MP3, .WMA, WMV, etc.
  • Removing unwanted application log, dump, or detail files from systems
Script Contents below:
on error resume next
Sub Usage()
WScript.Echo UCase(WScript.ScriptName) &_
" - Reports/Deletes files based on name and age" & vbNewLine & vbNewLine &_
"Enumerates files starting at a specified folder and evaluates there name and age against" & vbNewLine &_
"provided patterns and either reports them or optionally deletes them." & vbNewLine & vbNewLine &_
WScript.ScriptName & " -FOLDER <folder> [-PATTERN <regexpr>] [-DATEPATTERN <regexpr> -NUMDAYS <n>] [-DELETE]" & vbNewLine &_
"-FOLDER <folder>        Start the search at the specified folder" & vbNewLine &_
    "-PATTERN <regexpr>        Semicolon delimited regular expressions denoting files that should be flagged regardless of age." & vbNewLine &_
    "-DATEPATTERN <regexpr>    Semicolon delimited regular expressions denoting files that should be flagged based on age." & vbNewLine &_
    "-NUMDAYS <n>            Number of days that should be used as the threshold for the date flagged files, default is 3650" & vbNewLine &_
    "-DELETE                Flagged files should be deleted as well as reported" & vbNewLine &_
"-?, -H             Displays this help" & vbNewLine & vbNewLine
WScript.Quit
End Sub


sPattern = ""
sTmPattern=""
iNumDays = 3650
boolDelete = false


While i < WScript.Arguments.Count
Select Case Replace(UCase(WScript.Arguments.Item(i)),"/","-")
Case "-FOLDER":
        i=i+1
        objStartFolder=WScript.Arguments.Item(i)
    Case "-PATTERN":
        i=i+1
        sPattern=WScript.Arguments.Item(i)
    Case "-DATEPATTERN":
        i=i+1
        sTmPattern=WScript.Arguments.Item(i)
    Case "-NUMDAYS":
        i=i+1
        iNumDays=clng(WScript.Arguments.Item(i))
    case "-DELETE":
        boolDelete = true
Case "-?":
usage()
Case else:
usage()
End Select
i = i + 1
wend


if len(objStartFolder) = 0 then
    wscript.echo "Starting Folder not specified."
    wscript.quit -1
end if


Set objFSO = CreateObject("Scripting.FileSystemObject")


Set objFolder = objFSO.GetFolder(objStartFolder)
if err.number <> 0 then
    wscript.echo "Starting Folder: " & objStartFolder & " is not accessible or does not exist."
    wscript.quit -1
end if
' Wscript.Echo objFolder.Path
Set colFiles = objFolder.Files
For Each objFile in colFiles
    boolMatch = false
    if NameMatch(objFile.Name, sPattern) then
        boolMatch = true
    end if
    if not boolMatch then
        if NameMatch(objFile.Name, sTmPattern) then
            DaysOldMod = Int(Now() - objFile.DateLastModified)
            DaysOldCreate = Int(Now() - objFile.DateCreated)
            if (DaysOldCreate > iNumDays) and (DaysOldMod > iNumDays) then
                boolMatch = true
            end if
        end if
    end if
    if boolMatch then
        Wscript.Echo objStartFolder & "\" & objFile.Name
        if boolDelete then
            objFSO.DeleteFile objStartFolder & "\" & objFile.Name, true
            if err.number <> 0 then
                wscript.echo "Unable to delete file: " & objStartFolder & "\" & objFile.Name
                err.clear
            end if
        end if
    end if
Next
' Wscript.Echo


ShowSubfolders objFSO.GetFolder(objStartFolder)


Sub ShowSubFolders(Folder)
For Each Subfolder in Folder.SubFolders
' Wscript.Echo Subfolder.Path
Set objFolder = objFSO.GetFolder(Subfolder.Path)
Set colFiles = objFolder.Files
For Each objFile in colFiles
        boolMatch = false
        if NameMatch(objFile.Name, sPattern) then
            boolMatch = true
        end if
        if not boolMatch then
            if NameMatch(objFile.Name, sTmPattern) then
                DaysOldMod = Int(Now() - objFile.DateLastModified)
                DaysOldCreate = Int(Now() - objFile.DateCreated)
                if (DaysOldCreate > iNumDays) and (DaysOldMod > iNumDays) then
                    boolMatch = true
                end if
            end if
        end if
        if boolMatch then
            Wscript.Echo Subfolder.Path & "\" & objFile.Name
            if boolDelete then
                objFSO.DeleteFile Subfolder.Path & "\" & objFile.Name, true
                if err.number <> 0 then
                    wscript.echo "Unable to delete file: " & Subfolder.Path & "\" & objFile.Name
                err.clear
            end if
            end if
        end if
Next
' Wscript.Echo
ShowSubFolders Subfolder
Next
End Sub


function NameMatch(sName, sPattern)
    NameMatch=false
    if len(sPattern) = 0 then
        exit function
    end if
    
    set re = new regexp
    re.ignorecase=true
    
    arrPattern = split(sPattern, ";")
    for each pattern in arrPattern
        re.pattern = pattern
        if re.test(sName) then
            NameMatch=true
            exit function
        end if
    next
end function

No comments:

Post a Comment