AutoHotkey Community

It is currently May 27th, 2012, 3:38 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 34 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: Remove Empty Directories
PostPosted: October 27th, 2005, 12:57 am 
Offline

Joined: October 1st, 2005, 9:55 pm
Posts: 775
Location: Texas, USA
This script was created from an obscure and infrequent requirement that I have. Every once in a while I have a project where I end up with a bunch of extraneous (and usually) empty directories. It makes it much easier to go through all of the directories and subdirectories if I can find and delete the empty folders.

A couple of notes...

1. This script was designed to run from the command line in the directory where you want the search for empty directories to begin. You can't just double-click on the script and expect it to work the way you want it to. The script could easily be modified to request the starting folder. Since I use this routine infrequently, this enhancement would just slow me down, so... I didn't add it.

2. This routine will not delete an empty directory if a program or process has a hold on it. Windows Explorer does this every once in a while.

3. Directories that only contain empty directories are also deleted.

4. Not all empty directories are evil. Some are required. Please, I'm begging you, please use this script with care.

5. This script contains a bunch of code to display splash messages. Feel free to delete it. You don't really need it. I kept it in because it's a nice-to-have when dealing with large folder structures.

Here's the code. I hope you find it useful:
Code:
;RemoveEmptyDirectories.ahk

;-- Confirm
MsgBox 1,,About to remove all empty directories from:`r`n%A_WorkingDir%\
IfMsgBox Cancel
    return

;-- Initialize
$MinimumSplashTime=850


;[========================]
;[                        ]
;[  Build Directory List  ]
;[                        ]
;[========================]
;-- Splash
SplashImage,, W258 B2 ,,Building directory list...
$SplashStartTime=%A_TickCount%
Sleep 10  ;-- Allow splash image to render

;-- Build directory list
$DirectoryList=
Loop *.*,2,1
    $DirectoryList=%$DirectoryList%%A_LoopFileFullPath%`r`n

;-- Sort it
Sort $DirectoryList

;-- Compute elapsed time. Sleep if necessary
$SplashElapsedTime:=A_TickCount - $SplashStartTime
if $SplashElapsedTime<%$MinimumSplashTime%
    {
    $SleepTime:=$MinimumSplashTime - $SplashElapsedTime
    Sleep %$SleepTime%
    }


;[==================]
;[                  ]
;[  Remove Empties  ]
;[                  ]
;[==================]
;-- Splash
SplashImage,, W354 B2 ,,Searching for directories to delete...
$SplashStartTime=%A_TickCount%

;--- Loop until all empty directories have been deleted
$DeleteTotal=0
$LoopCount=0
Loop 99
    {
    $LoopCount:=$LoopCount + 1
    $DeleteCount=0
    Loop parse,$DirectoryList,`n,`r
        {
        if A_LoopField=  ;-- Ignore blank item
            continue
        IfExist %A_LoopField%
            {
            FileRemoveDir %A_LoopField%
            if errorlevel=0
               $DeleteCount:=$DeleteCount + 1
            }
        }
    $DeleteTotal:=$DeleteTotal + $DeleteCount
    If $DeleteCount=0
        break
    }

;-- Compute elapsed time. Sleep if necessary
$SplashElapsedTime:=A_TickCount - $SplashStartTime
if $SplashElapsedTime<%$MinimumSplashTime%
    {
    $SleepTime:=$MinimumSplashTime - $SplashElapsedTime
    Sleep %$SleepTime%
    }

;-- Splash off
SplashImage off


;[===========]
;[           ]
;[  Results  ]
;[           ]
;[===========]
If $DeleteTotal=0
    msgbox No empty directories found
 else
    msgbox %$DeleteTotal% empty directories removed


;-- Return to sender
return


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 27th, 2005, 2:44 am 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
Decarlo110 also posted a very long script for this. Yours is much shorter and very nicely laid out. However, we can make it even shorter
Code:
DelEmpty("C:\backup")

DelEmpty(dir)
{
   Loop %dir%\*.*, 2
      DelEmpty(A_LoopFileFullPath)
   FileRemoveDir %dir%
}
The trick is to try to delete the bottommost subdirectory in the directory tree, then the bottommost subdirectory of its siblings. When there are no more subdirectories in a level, we backtrack to higher levels in the tree. If a branch becomes empty, we prune it. In this order you don't have to sort or re-scan the tree. And the recursive calls ensure this depth-first traversal.

(Loop %dir%\*.*, 2, 1 scans the tree in breadth-first order, which prevents a two-line solution, but as you did, one can repeat this loop until no more directory is deleted. This is much slower, but still can be coded in less than 10 lines.)

Edit: fixed Decarlo110's name


Last edited by Laszlo on October 30th, 2005, 4:23 pm, edited 1 time in total.

Report this post
Top
 Profile  
Reply with quote  
PostPosted: October 27th, 2005, 5:13 am 
Offline

Joined: October 1st, 2005, 9:55 pm
Posts: 775
Location: Texas, USA
Neat trick. I'll just add a counter and it'll be a done deal. Thanks for the feedback.

One other thing I might add will be a check for hidden or system folders. May not want to delete them even if they are empty.

I did a search on this topic b/4 I posted. I'm surprised I didn't find the DeCarlo111 post. Obviously, didn't look hard enough.

Thanks.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 30th, 2005, 6:15 pm 
Offline

Joined: August 24th, 2005, 5:17 pm
Posts: 1237
I noticed that this function also deletes the main folder itself if empty, as well as sub-directories. To keep the main folder passed to the function, I made the following change:


Code:
DelEmpty_Folder = <<<put the folder name here>>>
DelEmpty(DelEmpty_Folder)

DelEmpty(dir) ; deletes empty sub-directories within the target
{
  Global DelEmpty_Folder
  Loop %dir%\*.*, 2
    {
    DelEmpty(A_LoopFileFullPath)
    }
  If dir !=%DelEmpty_Folder%
    {
    FileRemoveDir, %dir%
    }
}



p.s. This came in really handy for what I've been doing with jballi's playlist making script.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 30th, 2005, 7:28 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
...or you could just re-create the starting directory. FileCreateDir does nothing if the directory has not been deleted.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 30th, 2005, 10:41 pm 
Offline

Joined: August 24th, 2005, 5:17 pm
Posts: 1237
Yes I had debated about just re-creating the directory afterwards, but this way seemed a bit neater to me and less of a "fix".

I can't think of any real difference in the outcome of doing it either way, other than that this way it's all contained quite neatly in the function. I doubt there's any big speed difference as the hard disk operations would be the limiting factor in speed I would have thought.


Report this post
Top
 Profile  
Reply with quote  
PostPosted: October 31st, 2005, 12:05 am 
Offline

Joined: October 1st, 2005, 9:55 pm
Posts: 775
Location: Texas, USA
...or you call the function only after you begin to loop through the root folder. That way, the root folder is not deleted. Something like this:
Code:
Loop %p_UserDefinedDir%\*.*,2
    DelEmpty(A_LoopFileLongPath)


Them be my thoughts...


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 1st, 2005, 12:28 am 
Offline

Joined: August 24th, 2005, 5:17 pm
Posts: 1237
Actually I realised an even simpler solution to not deleting the starting directory, which is to change the FileRemoveDir path to %A_LoopFileFullPath%.

However, for the life of me, I can't figure out exactly how "%dir%" ends up containing the currently looped directory, or is this just some feature of "Loop" that I've overlooked?


Code:
DelEmpty_Folder =c:\folder_to_delete ; folder to process
DelEmpty(DelEmpty_Folder)
return

DelEmpty(dir)
{
   Loop %dir%\*.*, 2
     DelEmpty(A_LoopFileFullPath)
   FileRemoveDir %A_LoopFileFullPath%
}


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 1st, 2005, 1:05 am 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
%dir% references the function parameter, which does not need the % signs around it when it is in the parameter list or in expressions, but it needs the % signs if it represents a variable.

The starting directory is not deleted with A_LoopFileFullPath or A_LoopFileLongPath, because these loop variables become empty outside of the loop. In deeper recursion levels (not in a loop) they look like global variables (no local value is assigned to them), so they inherit the value of the variable in the calling function. It happens to be the same as dir, the function argument, except at the topmost level. There it refers to a global variable, which normally does not exist.

This is how it should work, which shows how good a job Chris did. However, there is a danger: if there is a global variable A_LoopFileFullPath or A_LoopFileLongPath, the corresponding (empty) directories will be deleted. It happens if you call the DelEmpty function from a loop. The other two proposals don't have this problem.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 1st, 2005, 3:35 am 
Offline

Joined: August 24th, 2005, 5:17 pm
Posts: 1237
Laszlo,

Yes I did understand that already about referencing variables with %, etc - the specific part that I couldn't really see, was how that variable is given a new value in this function, since it isn't assigned one directly. (I understand why A_LoopFileFullPath, etc get new values because of the way they are linked to the Loop command, but I can't see how this is the case with "dir")

I followed your explanation about my last suggestion - I guess it's probably best not to do it that way really then. I've been noticing how AutoHotKey likes to "misbehave" in other situations when it doesn't get valid paths, etc, such as traversing through a whole drive instead of aborting (kind of worrying, depending on what file operations you're doing!)

"if there is a global variable A_LoopFileFullPath or A_LoopFileLongPath" - I thought that those variables were only valid inside of loops and couldn't be defined as global anyhow?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 1st, 2005, 4:33 am 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
Inside the function the parameter, whatever it was at the call, gets a new name: dir, in our case. It is an implicit assignment, like

(local copy of) dir := parameter (at the function call)

You are right, A_LoopFileFullPath or A_LoopFileLongPath are not allowed to be changed or assigned a value. But they are defined in file loops, when they would look as normal variable. If inside that loop you call a function, it sees them as normal global variables.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 2nd, 2005, 6:18 pm 
Offline

Joined: August 24th, 2005, 5:17 pm
Posts: 1237
Thanks for your explanation, think that cleared things up for me (especially the implicit assignment bit, surprised that isn't mentioned in the manual - not that I could see at least).


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 2nd, 2005, 8:45 pm 
Offline

Joined: December 15th, 2004, 10:24 pm
Posts: 303
Location: United States
evl wrote:
that cleared things up for me (especially the implicit assignment bit, surprised that isn't mentioned in the manual

Purposeful use of any function requires use of labels to manipulate contents of passed parameters. Since it is impractical to require using the same variable name for the function and the caller, and it is impossible for the function writer to know the desired variable names of the function user, the assignment of the function's variable label to the passed parameter contents is naturally required.

---

By the way, the 6-line solution becomes unusable if ever you require a user prompt as to what to do with identified empty directories. Further, code size increases exponentially to handle identification + user prompt. It is possible to add 2 lines to the 6-line solution to identify the folders. There's one small problem with that.. the folders are already deleted and any prompting for further handling becomes meaningless.

_________________
1) The Open Source Definition http://www.opensource.org/docs/definition_plain.php

2) Intuitive. Logical. Versatile. Adaptable. <<AutoHotkey>>


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 2nd, 2005, 10:30 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
Decarlo110 wrote:
the folders are already deleted and any prompting for further handling becomes meaningless
It is, of course, not true. If the user wants to keep a particular empty directory, we simply re-create it. That is, check the ErrorLevel after FileRemoveDir %dir%. If it shows that the directory was not deleted, we just go on. If ErrorLevel = 0, we pop up a MsgBox,4… to ask the user, if s/he wants to keep it, and if it is necessary: FileCreateDir %dir%. This makes the function 10 lines long. (With FileSetTime and FileSetAttrib you could make them indistinguishable from the original, if needed.)

If you want to display the directory attributes, its creation or last access time, they add an extra line each. You could also update a list of deleted directories, a list of empty directories kept, or all the directories, they also add a line to the script each. Showing this statistics at the end needs another line, but all together the script must be shorter than 20 lines.

You can make it, however, arbitrarily complex with progress bars, GUI's of different colors and font sizes, defining hotkeys for specific actions, saving defaults in .ini files, showing help or "about" windows, etc. These could make sense if you want a uniform look of several tools, or certain hotkeys should perform a similar action in them, or, if you want to sell an application, which should look complex, professional and worth the money. We must not forget, however, that the underlying code is only a handful AHK commands.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 2nd, 2005, 11:20 pm 
Offline

Joined: December 15th, 2004, 10:24 pm
Posts: 303
Location: United States
Laszlo wrote:
Decarlo110 wrote:
the folders are already deleted and any prompting for further handling becomes meaningless

It is, of course, not true. If the user wants to keep a particular empty directory, we simply re-create it. That is, check the ErrorLevel after FileRemoveDir %dir%. If it shows that the directory was not deleted, we just go on. If ErrorLevel = 0, we pop up a MsgBox,4… to ask the user, if s/he wants to keep it, and if it is necessary: FileCreateDir %dir%. This makes the function 10 lines long. (With FileSetTime and FileSetAttrib you could make them indistinguishable from the original, if needed.)

My understanding of ErrorLevel=0 in this case is that the folder has actually been deleted, or is in the irreversible queue of O/S commands in waiting. Use of FileSetTime and FileSetAttrib can be applied after use of FileGetTime + FileGetAttrib... the puzzle is in the FileName parameter which is to be supplied.... If however, a command physically beneath FileRemoveDir is able to act prior to FileRemoveDir's completion, i was not aware of it. If you can verify your 20-line claim, i will stand corrected.

_________________
1) The Open Source Definition http://www.opensource.org/docs/definition_plain.php

2) Intuitive. Logical. Versatile. Adaptable. <<AutoHotkey>>


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 34 posts ]  Go to page 1, 2, 3  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: xXDarknessXx and 15 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group