Deleteing empty directories

C

CinnamonDonkey

Hi All,

I've been scratching my head all afternoon trying to work out the best/
quickest way is to delete empty directories within a tree (Windows).

I've looked at os.walk() but it seems to traverse the directory tree
in the wrong order (is it possible to reverse the order?)

It seems the only way is to manually walk the tree myself recursively
and then back up deleteing a directory if it is found to be empty.

Any ideas?

Cheers,
SHaun >8)
 
T

Tim Golden

CinnamonDonkey said:
Hi All,

I've been scratching my head all afternoon trying to work out the best/
quickest way is to delete empty directories within a tree (Windows).

I've looked at os.walk() but it seems to traverse the directory tree
in the wrong order (is it possible to reverse the order?)

It seems the only way is to manually walk the tree myself recursively
and then back up deleteing a directory if it is found to be empty.

In general, the place to look for these things in the
stdlib is usually shutil. (Slightly awkward that
"shell" in Windows means everything that happens on
the desktop, while "shell" in Unix means everything
*except* what happens on the desktop! This is the
Unix meaning.)

And sure enough...

"""
rmtree( path[, ignore_errors[, onerror]])

Delete an entire directory tree (path must point to a directory). If ignore_errors is true, errors resulting from failed removals will be ignored; if false or omitted, such errors are handled by calling a handler specified by onerror or, if that is omitted, they raise an exception.
If onerror is provided, it must be a callable that accepts three parameters: function, path, and excinfo. The first parameter, function, is the function which raised the exception; it will be os.listdir(), os.remove() or os.rmdir(). The second parameter, path, will be the path name passed to function. The third parameter, excinfo, will be the exception information return by sys.exc_info(). Exceptions raised by onerror will not be caught.

"""


TJG
 
C

CinnamonDonkey

My understanding was that rmtree removes a whole tree not just the
empty directories?

eg.

root
- file1
- file2
- dir1
- dir2
- file3
- dir3

I would expect; dir1 and dir3 to be deleted and nothing else touched.

My attempt came up with:

import os
import shutil

def isDirEmpty( path ):
if not os.path.isdir( path ):
return False

contents = os.listdir( path )

if len(contents) == 0:
return True

return False


def RecurseTree( path ):
if not os.path.isdir( path ):
return False

contents = os.listdir( path )

if len(contents) == 0:
print "Deleting Empty Dir '%s'" % (path,)
#shutil.rmtree(path)

else:
for item in contents:
investigate = "%s\\%s" % (path, item)
if os.path.isdir(investigate):
RecurseTree( investigate )


if __name__ == '__main__':
RecurseTree( r"c:\temp" )


But I'm not sure what the max recursion depth is in python? Plus I
think this could be more efficient.




CinnamonDonkey said:
I've been scratching my head all afternoon trying to work out the best/
quickest way is to delete empty directories within a tree (Windows).
I've looked at os.walk() but it seems to traverse the directory tree
in the wrong order (is it possible to reverse the order?)
It seems the only way is to manually walk the tree myself recursively
and then back up deleteing a directory if it is found to be empty.

In general, the place to look for these things in the
stdlib is usually shutil. (Slightly awkward that
"shell" in Windows means everything that happens on
the desktop, while "shell" in Unix means everything
*except* what happens on the desktop! This is the
Unix meaning.)

And sure enough...

"""
rmtree( path[, ignore_errors[, onerror]])

Delete an entire directory tree (path must point to a directory). If ignore_errors is true, errors resulting from failed removals will be ignored; if false or omitted, such errors are handled by calling a handler specified by onerror or, if that is omitted, they raise an exception.
If onerror is provided, it must be a callable that accepts three parameters: function, path, and excinfo. The first parameter, function, is the function which raised the exception; it will be os.listdir(), os.remove() or os..rmdir(). The second parameter, path, will be the path name passed to function. The third parameter, excinfo, will be the exception information return by sys.exc_info(). Exceptions raised by onerror will not be caught.

"""

TJG
 
C

CinnamonDonkey

Revised:

root
+ Dir1
+ Dir2
+ Dir3
+ NotEmptyDir
File1
File2

Result:

Root
+ NotEmptyDir
File1
File2



---


import os
import shutil

def isDirEmpty( path ):
if not os.path.isdir( path ):
return False

contents = os.listdir( path )

if len(contents) == 0:
return True

return False


def RecurseTree( path ):
print "RecurseTree( %s )" % (path,)

if not os.path.isdir( path ):
print "RecurseTree( %s ) - NOT A DIRECTORY" % (path,)
return 0

contents = os.listdir( path )

for item in contents:
investigate = "%s\\%s" % (path, item)
if os.path.isdir(investigate):
RecurseTree( investigate )

contents = os.listdir( path )

if len(contents) == 0:
print "Deleting Empty Dir '%s'" % (path,)
shutil.rmtree(path)



if __name__ == '__main__':
RecurseTree( r"c:\temp" )


My understanding was that rmtree removes a whole tree not just the
empty directories?

eg.

root
 - file1
 - file2
 - dir1
 - dir2
  - file3
  - dir3

I would expect; dir1 and dir3 to be deleted and nothing else touched.

My attempt came up with:

import os
import shutil

def isDirEmpty( path ):
    if not os.path.isdir( path ):
        return False

    contents = os.listdir( path )

    if len(contents) == 0:
        return True

    return False

def RecurseTree( path ):
    if not os.path.isdir( path ):
        return False

    contents = os.listdir( path )

    if len(contents) == 0:
        print "Deleting Empty Dir '%s'" % (path,)
        #shutil.rmtree(path)

    else:
        for item in contents:
            investigate = "%s\\%s" % (path, item)
            if os.path.isdir(investigate):
                RecurseTree( investigate )

if __name__ == '__main__':
    RecurseTree( r"c:\temp" )

But I'm not sure what the max recursion depth is in python? Plus I
think this could be more efficient.

In general, the place to look for these things in the
stdlib is usually shutil. (Slightly awkward that
"shell" in Windows means everything that happens on
the desktop, while "shell" in Unix means everything
*except* what happens on the desktop! This is the
Unix meaning.)
And sure enough...
"""
rmtree( path[, ignore_errors[, onerror]])
Delete an entire directory tree (path must point to a directory). If ignore_errors is true, errors resulting from failed removals will be ignored; if false or omitted, such errors are handled by calling a handler specified by onerror or, if that is omitted, they raise an exception.
If onerror is provided, it must be a callable that accepts three parameters: function, path, and excinfo. The first parameter, function, is the function which raised the exception; it will be os.listdir(), os.remove() or os.rmdir(). The second parameter, path, will be the path name passed to function. The third parameter, excinfo, will be the exception information return by sys.exc_info(). Exceptions raised by onerror will not be caught.

TJG
 
S

Steven D'Aprano

My understanding was that rmtree removes a whole tree not just the empty
directories?


So it seems:

I think what you want is os.removedirs().
os.makedirs('root/die-die-die/empty/empty/empty')
os.listdir('root') ['keep', 'die-die-die']
os.removedirs('root/die-die-die/empty/empty/empty')
os.listdir('root')
['keep']




def isDirEmpty( path ):
if not os.path.isdir( path ):
return False

contents = os.listdir( path )

if len(contents) == 0:
return True

return False

That can be simplified to:

# untested
def isDirEmpty(path):
return os.path.isdir(path) and not len(os.listdir(path))


def RecurseTree( path ):
if not os.path.isdir( path ):
return False

What if it is a symbolic link to a directory?
contents = os.listdir( path )

if len(contents) == 0:
print "Deleting Empty Dir '%s'" % (path,) #shutil.rmtree(path)

Why do you go to the trouble of defining isDirEmpty() and then not use it?
else:
for item in contents:
investigate = "%s\\%s" % (path, item) if
os.path.isdir(investigate):
RecurseTree( investigate )

As soon as you start recursively walking over directories, you should use
os.walk. It will almost certainly do what you want.

if __name__ == '__main__':
RecurseTree( r"c:\temp" )


But I'm not sure what the max recursion depth is in python?

By default in my version:
1000

but it can be changed.

Plus I think this could be more efficient.

Probably, but why do you care? The file I/O probably will take 99% of the
time, and I doubt you can improve that.

Of course I could be wrong, so profile, profile, profile, and find out
where the time really is being spent.
 
C

CinnamonDonkey

Steven you are right, isDirEmpty() isn't even used. That's what
happens when you try to get a last minute thread going 5 minutes
before home time! ;-)



Thanx for the responses guys! It's been very useful :)




My understanding was that rmtree removes a whole tree not just the empty
directories?

So it seems:

I think what you want is os.removedirs().

['keep', 'die-die-die']>>> os.removedirs('root/die-die-die/empty/empty/empty')
os.listdir('root')
['keep']

def isDirEmpty( path ):
    if not os.path.isdir( path ):
        return False
    contents = os.listdir( path )
    if len(contents) == 0:
        return True
    return False

That can be simplified to:

# untested
def isDirEmpty(path):
    return os.path.isdir(path) and not len(os.listdir(path))
def RecurseTree( path ):
    if not os.path.isdir( path ):
        return False

What if it is a symbolic link to a directory?
    contents = os.listdir( path )
    if len(contents) == 0:
        print "Deleting Empty Dir '%s'" % (path,) #shutil.rmtree(path)

Why do you go to the trouble of defining isDirEmpty() and then not use it?
    else:
        for item in contents:
            investigate = "%s\\%s" % (path, item) if
            os.path.isdir(investigate):
                RecurseTree( investigate )

As soon as you start recursively walking over directories, you should use
os.walk. It will almost certainly do what you want.
if __name__ == '__main__':
    RecurseTree( r"c:\temp" )
But I'm not sure what the max recursion depth is in python?

By default in my version:

1000

but it can be changed.
Plus I think this could be more efficient.

Probably, but why do you care? The file I/O probably will take 99% of the
time, and I doubt you can improve that.

Of course I could be wrong, so profile, profile, profile, and find out
where the time really is being spent.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,997
Messages
2,570,241
Members
46,831
Latest member
RusselWill

Latest Threads

Top