How Can I Delete a Folder and All Its Subfolders?

How Can I Delete a Folder and All Its Subfolders?

  • Comments 11
  • Likes
Hey, Scripting Guy! Question

Hey Scripting Guy! How can I delete a folder and all its subfolders?

-- FB

SpacerHey, Scripting Guy! AnswerScript Center

Hey, FB. You know, a lot of people think the Scripting Guys know everything there is to know about scripting. Let’s put it this way: we wish. In fact, it’s only when we begin to think that we do know something about scripting that we get ourselves into trouble.

Take (please!) this seemingly-simple little question about deleting a folder and all its subfolders. One of the Scripting Guys (Greg asked us not to mention his name, to save him from embarrassment) thought this was easy; in fact, he already had a script that could delete a folder and its subfolders:

strComputer = "."
Set objWMIService = GetObject("winmgmts: \\" & strComputer & "\root\cimv2")

Set colFolders = objWMIService.ExecQuery _
    ("Select * from Win32_Directory where Name = 'c:\\Scripts'")

For Each objFolder in colFolders
    errResults = objFolder.Delete
Next

Our unnamed Scripting Guy grabbed the script, did a quick test on his Windows XP Service Pack 1 computer and on a Windows 2000 computer, and discovered that the script worked just fine. With yet another job well done, he then took the rest of the day off and began planning how he would spend the huge bonus he would undoubtedly receive for having successfully solved this problem.

It was only later that another Scripting Guy (and - alas - the newest member of the team at that) pointed out that his script - which worked perfectly well back in the Dark Ages of computing - no longer functioned in the modern world; in particular, it failed on any Windows XP computer running Service Pack 2. Why? Well, in Service Pack 1 you delete a folder and all its contents, including any subfolders, with no questions asked. In Service Pack 2, however, the behavior of the Win32_Directory class has changed. On a computer running SP2, a folder cannot be deleted if it contains even a single subfolder; instead, you generate a “Folder not empty” error. Ouch! In fact, using WMI the only way to delete a folder in SP2 is to first delete all its subfolders. And, of course, before you can delete a subfolder you have to delete all of its subfolders. And before you can delete any of those - well, it just goes on and on from there.

Of course a lesser person would have given up at that point, and just told you that, “There’s no way to delete a folder and all its contents.” And that’s exactly what our anonymous Scripting Guy was going to do. However, under intense pressure from his colleagues, he came up with this solution instead, one that creates a list of the entire folder tree and then methodically deletes folders from the bottom to the top. And don’t worry, we’ll tell you what that all means in a minute.

Here’s a script that ought to work on any version of Windows (or at least any version of Windows with WMI installed):

Dim arrFolders()
intSize = 0

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

strFolderName = "c:\scripts\archive"

Set colSubfolders = objWMIService.ExecQuery _
    ("Associators of {Win32_Directory.Name='" & strFolderName & "'} " _
        & "Where AssocClass = Win32_Subdirectory " _
            & "ResultRole = PartComponent")

ReDim Preserve arrFolders(intSize)
arrFolders(intSize) = strFolderName
intSize = intSize + 1

For Each objFolder in colSubfolders
    GetSubFolders strFolderName
Next

Sub GetSubFolders(strFolderName)
    Set colSubfolders2 = objWMIService.ExecQuery _
        ("Associators of {Win32_Directory.Name='" & strFolderName & "'} " _
            & "Where AssocClass = Win32_Subdirectory " _
                & "ResultRole = PartComponent")

    For Each objFolder2 in colSubfolders2
        strFolderName = objFolder2.Name
        ReDim Preserve arrFolders(intSize)
        arrFolders(intSize) = strFolderName
        intSize = intSize + 1
        GetSubFolders strFolderName
    Next
End Sub

For i = Ubound(arrFolders) to 0 Step -1
    strFolder = arrFolders(i)
    strFolder = Replace(strFolder, "\", "\\")
    Set colFolders = objWMIService.ExecQuery _
        ("Select * from Win32_Directory where Name = '" & strFolder & "'")

    For Each objFolder in colFolders
        errResults = objFolder.Delete
    Next
Next

We won’t even attempt to walk through this code line-by-line; instead we’ll just give you a rough idea of what we’re doing, and why, and point you to some additional resources if you need more information.

To begin with, let’s say C:\Scripts\Archive looks like this:

Folder Tree

We can’t delete the Archive folder right away; that’s because of Subfolder A1 and Subfolder A2. And we can’t delete Subfolder A1 right away; that’s because of Subfolder B1. To get rid of the Archive folder we need to:

Delete Subfolder A2.

Delete Subfolder B1, clearing out Subfolder A1.

Delete Subfolder A1. With both Subfolder A1 and Subfolder A2 gone, we’ll then be free to delete the Archive folder.

Delete the Archive folder.

The approach we’ve taken makes use of a recursive function; this function can identify all the subfolders of a folder, as well as all the subfolders of those subfolders. We won’t discuss this function in any detail today; for an explanation of how it works, check out this Hey, Scripting Guy! column.

By necessity, our recursive function starts with the first folder - Archive - and then works its way down the tree. Unfortunately, that’s a bit of a problem; after all, we need to start deleting folders from the bottom of the tree and work our way up. There are probably lots of ways to handle this problem, but what we did was elect not to try deleting folders right away; instead, all we do is stash the folder paths into an array named arrFolders. Each time we encounter a new folder or subfolder we add the folder path to the array. When the recursive function finishes, our array will look like this:

C:\Scripts\Archive
C:\Scripts\Archive\Subfolder A1
C:\Scripts\Archive\Subfolder A1\Subfolder B1
C:\Scripts\Archive\Subfolder A2

Note. We used a so-called “dynamic” array in this script; for more information about dynamic arrays - and about weird-looking commands like Redim Preserve - please see this portion of the Microsoft Windows 2000 Scripting Guide.

Trust us: we’re well aware that this was way easier in Windows 2000. But at least now we’re ready to start deleting folders. If you look at the items in our array, they happen to be exactly opposite of what we need; for example, the Archive folder is the first item in our array, but it has to be the last item that we delete. If we could invert the array - making the first last and the last first, to steal a phrase - we’d be in business. That’s where this block of code comes in:

For i = Ubound(arrFolders) to 0 Step -1
    strFolder = arrFolders(i)
    strFolder = Replace(strFolder, "\", "\\")
    Set colFolders = objWMIService.ExecQuery _
        ("Select * from Win32_Directory where Name = '" & strFolder & "'")

    For Each objFolder in colFolders
        errResults = objFolder.Delete
    Next
Next

What we’re doing here is reading our array from the bottom up. We’re creating a loop that starts with the very last item in the array; that’s what the Ubound (upper bound) function is for. We’re then going to work our way down to the first item in the array: item 0. (As you might recall, the first element in an array is always item 0, not item 1.) The Step -1 function just means that we step backwards rather than forward: instead of going 0-1-2-3, we’re going 3-2-1-0. This is how we can start deleting at the bottom of the tree.

Of course, before we can do that we need to adjust the folder paths; that’s what we do here:

strFolder = Replace(strFolder, "\", "\\")

Our folder paths are going to look like this: C:\Scripts\Archive\Subfolder A1\Subfolder B1. That’s fine, except that we need to include these paths in a WQL query. Consequently, we need to double up all the \’s, resulting in paths that look like this: C:\\Scripts\\Archive\\Subfolder A1\\Subfolder B1. We use the Replace function to replace each \ with \\.

You’re right: there’s nothing straightforward about this script, is there?

With our new \\ folder paths we can then use WMI to connect to the folder in question and - at long last - use the Delete method to actually delete the folder. That’s what happens here:

Set colFolders = objWMIService.ExecQuery _
    ("Select * from Win32_Directory where Name = '" & strFolder & "'")

For Each objFolder in colFolders
    errResults = objFolder.Delete
Next

Having disposed of Subfolder B1, we can then start working our way up the tree, eventually deleting C:\Scripts\Archive.

Five million lines of code later, we’re done, and C:\Scripts\Archive has been deleted. We’re not saying it’s a particularly elegant solution, but it is a solution, and it will work on all versions of Windows. And because it uses WMI, it works equally well on remote machines as it does on the local computer.

So there you have it: deleting a folder and all its subfolders, a subject we’ve vowed never to deal with again. Well, at least not until the next service pack comes out ….


Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • function CountChar(S,C)

    cc = 0

    st = 1

    Do

     i = instr(st, S, C)

     st = i + 1

     if i > 0 then cc = cc + 1

    Loop While i > 0

    CountChar = cc

    end function

    'Delete profile for users whose name starts from UserNameBase

    sub delFolders(strComputer)

    Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

    'Path and drive conditions are to speed up the query

    Set colFolders = objWMIService.ExecQuery ("select * from Win32_Directory where path like '\\Documents and Settings\\%' and drive = 'C:'  and Name LIKE 'C:\\Documents and Settings\\" & UserNameBase & "%'")

    MaxLevel = 1

    MinLevel = 2

    'Find out the level of deepest folder

    For Each objFolder in colFolders

       cc = CountChar(objFolder.Name, "\")

       if cc > MaxLevel then MaxLevel = cc

    'spLog.InnerHTML = spLog.InnerHTML & "Deleting profile " & objFolder.Name & " - " & cc & "<br>" & vbCrlf

    Next

    'Delete startting from deepest folder

    For i = MaxLevel to MinLevel Step -1

     For Each objFolder in colFolders

         cc = CountChar(objFolder.Name, "\")

     if cc = i then

        spLog.InnerHTML = spLog.InnerHTML & "Deleting " & objFolder.Name

            errResults = objFolder.Delete

            spLog.InnerHTML = spLog.InnerHTML & " [" & errResults & "]<br>" & vbCrlf

     end if

     Next

    Next

    end sub

  • this script fails for me on the first line of code:

    dim arrFolders ()

    An expression was expected after '('.

    At :line:1 char:17

    + dim arrFolders () <<<<

    Any recommendations?

  • No need to declare the array explicitly when ReDim is used. Declare as below.

    dim arrFolders ()

    Dim arrFolders

    -Ram

  • Why all this complexity when this works just fine?

    set objFSO = CreateObject ("Scripting.FileSystemObject")

    set objFolder = objFSO.GetFolder("C:\Folder")

    objFolder.Delete

  • a rewrite of above, using a recursive function.  primary benefit being increased portability:

    Function DeleteFolderTree( strComputer, strFolderName )

    'Delete the specified folder (and all subfolders) from the specified computer.

    ' Since The WMI command won't natively remove folders that have subfolders,

    ' this is structured as a recursive function.  that is, DeleteFolderTree queries

    ' the named folder for all subfolders, then calls itself for each subfolder found.  

    ' When it gets to the end of the tree (no more subfolders) it starts deleting.

    on error resume next

    'Dim arrFolders()

    'intSize = 0

    'we'll keep strFolderName the friendly "\" version, and strWMIPath the "\\" version

    strFolderName = Replace( strFolderName, "\\", "\" )

    strWMIPath = Replace( strFolderName, "\", "\\" )

    'strComputer = "."

    Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

    'Before we can delete this folder, we have to delete its subfolders.  

    ' first identify subfolders

    strWMIQuery = "Associators of {Win32_Directory.Name='" & strFolderName & "'} " _

    & "Where AssocClass = Win32_Subdirectory " _

    & "ResultRole = PartComponent"

    Set colFolders = objWMIService.ExecQuery( strWMIQuery )

    'now recurse through the subfolders and delete them (...recursively)

    intFolderCount = colFolders.Count

    If (intFolderCount > 0) AND (err.Number = 0) Then

    'subfolders exist.  delete them.

    For Each objFolder in colFolders

    strResults = strResults & DeleteFolderTree( strComputer, objFolder.Name )

    Next

    End If 'colFolders.count > 0

    'once we get this far in the code we know there are no more subfolders for this folder,

    ' reuse colFolders object to get a handle on this folder (previously was a handle on subfolders)

    Set colFolders = objWMIService.ExecQuery ( "Select * From Win32_Directory Where Name = '" & strWMIPath & "'" )

    intFolderCount = colFolders.Count

    If (intFolderCount > 0) AND (err.Number = 0) Then

    'query returned without error, meaning the folder exists and is ready.  delete each (1) instance found

    For Each objFolder in colFolders

    intResults = objFolder.Delete

    Next

    'finally, return results

    If intResults = 0 Then 'success

    DeleteFolderTree = strResults & "Deleted """ & strFolderName & """.  "

    Else

    DeleteFolderTree = "Error """ & strResults & """ Deleting """ & strFolderName & """. "

    End If

    Else

    'This folder doesn't exist.

    DeleteFolderTree = "Folder """ & strFolderName & """ not found.  "

    End If 'colFolders.count > 0

    End Function 'DeleteFolderTree

  • A useful tidbit of information that had me pulling my hair out a bit when trying to delete user profiles: If a directory name contains a curly brace (i.e. { or } character) then the "Associators of" query will return an error.  To get around this, you can encapsulate the folder name in double quotes instead of single quotes, BUT you need to replace backslashes in the path with double backslashes...

    Set colSubfolders2 = objWMIService.ExecQuery _

           ("Associators of {Win32_Directory.Name=""" & REPLACE(strFolderName,"\","\\") & """} " _

               & "Where AssocClass = Win32_Subdirectory " _

                   & "ResultRole = PartComponent")

    This could apply to other special characters so it might be best just to stick with the above syntax to be on the safe side. YMMV.

    us.generation-nt.com/wmi-object-paths-containing-right-brace-character-help-26554782.html

  • This script is perfect. can you insert a line that only deletes folders and files that were last modified more then 30 days ago?

  • Hey Script guy,

    Can you please share a script to delete the subfolder older than x day

    c:\xyz\user1\temp\1234

    c:\xyz\user2\temp\2345

    I have 50 user folders  and I want delete the folders 123, 2345 .. Please help

  • Is it possible to set a system variable for this script for the directory to be deleted such as %systemdrive%\scripts\archive ? If so, would you provide an example. Thank you.

  • Dim FS : Set FS = CreateObject("Scripting.FileSystemObject")

    Private Sub deleteRecursive(path)
    Dim folder, file, subFolder

    If FS.FolderExists(path) Then
    Set folder = FS.GetFolder(path)

    ' remove subfolders:
    For Each subFolder In Folder.SubFolders
    Call deleteRecursive(subFolder)
    Next

    ' remove files
    For Each file In Folder.Files
    Print "removing: [f] " & file.path '# file.name
    FS.DeleteFile(file.path)
    Next

    ' ... and remove folder (empty now)
    Print "removing: [d] " & path
    FS.DeleteFolder(path)

    ElseIf FS.FileExists(path) Then
    Print "removing: [f] " & path
    FS.DeleteFile(path)
    Else
    Print "No path found: " & path
    End If
    End Sub

    Sub Print(msg)
    WScript.StdOut.WriteLine msg
    End Sub

    '########### MAIN:

    Call deleteRecursive("C:\Users\delgado\asdf")

  • Please read the "description" carefully:

    FileSystemObject: DeleteFolder Method
    http://msdn.microsoft.com/en-us/library/aa265021%28v=vs.60%29.aspx

    ---> "Deletes a specified folder AND its contents."

    Well, shorter than scott wrote:

    set objFSO = CreateObject ("Scripting.FileSystemObject")
    objFSO.DeleteFolder("C:\Folder")

    DOES IT ALL!