 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
jballi
Joined: 01 Oct 2005 Posts: 747 Location: Texas, USA
|
Posted: Wed Oct 26, 2005 11:57 pm Post subject: Remove Empty Directories |
|
|
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
|
|
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4710 Location: Boulder, CO
|
Posted: Thu Oct 27, 2005 1:44 am Post subject: |
|
|
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 Sun Oct 30, 2005 3:23 pm; edited 1 time in total |
|
| Back to top |
|
 |
jballi
Joined: 01 Oct 2005 Posts: 747 Location: Texas, USA
|
Posted: Thu Oct 27, 2005 4:13 am Post subject: Re: Remove Empty Directories |
|
|
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. |
|
| Back to top |
|
 |
evl
Joined: 24 Aug 2005 Posts: 1237
|
Posted: Sun Oct 30, 2005 5:15 pm Post subject: |
|
|
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. |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4710 Location: Boulder, CO
|
Posted: Sun Oct 30, 2005 6:28 pm Post subject: |
|
|
| ...or you could just re-create the starting directory. FileCreateDir does nothing if the directory has not been deleted. |
|
| Back to top |
|
 |
evl
Joined: 24 Aug 2005 Posts: 1237
|
Posted: Sun Oct 30, 2005 9:41 pm Post subject: |
|
|
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. |
|
| Back to top |
|
 |
jballi
Joined: 01 Oct 2005 Posts: 747 Location: Texas, USA
|
Posted: Sun Oct 30, 2005 11:05 pm Post subject: Re: Remove Empty Directories |
|
|
...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... |
|
| Back to top |
|
 |
evl
Joined: 24 Aug 2005 Posts: 1237
|
Posted: Mon Oct 31, 2005 11:28 pm Post subject: |
|
|
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%
}
|
|
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4710 Location: Boulder, CO
|
Posted: Tue Nov 01, 2005 12:05 am Post subject: |
|
|
%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. |
|
| Back to top |
|
 |
evl
Joined: 24 Aug 2005 Posts: 1237
|
Posted: Tue Nov 01, 2005 2:35 am Post subject: |
|
|
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? |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4710 Location: Boulder, CO
|
Posted: Tue Nov 01, 2005 3:33 am Post subject: |
|
|
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. |
|
| Back to top |
|
 |
evl
Joined: 24 Aug 2005 Posts: 1237
|
Posted: Wed Nov 02, 2005 5:18 pm Post subject: |
|
|
| 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). |
|
| Back to top |
|
 |
Decarlo110
Joined: 15 Dec 2004 Posts: 303 Location: United States
|
Posted: Wed Nov 02, 2005 7:45 pm Post subject: |
|
|
| 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>> |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4710 Location: Boulder, CO
|
Posted: Wed Nov 02, 2005 9:30 pm Post subject: |
|
|
| 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. |
|
| Back to top |
|
 |
Decarlo110
Joined: 15 Dec 2004 Posts: 303 Location: United States
|
Posted: Wed Nov 02, 2005 10:20 pm Post subject: |
|
|
| 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>> |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|