Reputation: 9022
I'm having a problem with deleting empty directories. Here is my code:
for dirpath, dirnames, filenames in os.walk(dir_to_search):
# other codes
try:
os.rmdir(dirpath)
except OSError as ex:
print(ex)
The argument dir_to_search
is where I'm passing the directory where the work needs to be done. That directory looks like this:
test/20/...
test/22/...
test/25/...
test/26/...
Note that all the above folders are empty. When I run this script the folders 20
,25
alone gets deleted! But the folders 25
and 26
aren't deleted, even though they are empty folders.
The exception that I'm getting are:
[Errno 39] Directory not empty: '/home/python-user/shell-scripts/s3logs/test'
[Errno 39] Directory not empty: '/home/python-user/shell-scripts/s3logs/test/2012'
[Errno 39] Directory not empty: '/home/python-user/shell-scripts/s3logs/test/2012/10'
[Errno 39] Directory not empty: '/home/python-user/shell-scripts/s3logs/test/2012/10/29'
[Errno 39] Directory not empty: '/home/python-user/shell-scripts/s3logs/test/2012/10/29/tmp'
[Errno 39] Directory not empty: '/home/python-user/shell-scripts/s3logs/test/2012/10/28'
[Errno 39] Directory not empty: '/home/python-user/shell-scripts/s3logs/test/2012/10/28/tmp'
[Errno 39] Directory not empty: '/home/python-user/shell-scripts/s3logs/test/2012/10/26'
[Errno 39] Directory not empty: '/home/python-user/shell-scripts/s3logs/test/2012/10/25'
[Errno 39] Directory not empty: '/home/python-user/shell-scripts/s3logs/test/2012/10/27'
[Errno 39] Directory not empty: '/home/python-user/shell-scripts/s3logs/test/2012/10/27/tmp'
Where am I making a mistake?
Upvotes: 355
Views: 353221
Reputation: 7444
Try shutil.rmtree
to delete files and directories:
import shutil
shutil.rmtree('/path/to/your/dir/')
Upvotes: 732
Reputation: 1846
I like the answers by @buhtz and @pepoluan the best for their use of pathlib.Path
and an iterative solution instead of recursion. However, they can be problematic if there are multiple levels of directories. All of the files may be deleted, but then the loop will start on the what may be the top level non-empty directory that contains only sub-directories, no files, and fail.
In Python 3.12, a walk method has been added to Path
that lets one walk bottom-up to avoid this problem.
The sample code implementing shutil.rmtree
from the documentation is repeated here for convenience:
# Delete everything reachable from the directory "top".
# CAUTION: This is dangerous! For example, if top == Path('/'),
# it could delete all of your files.
for root, dirs, files in top.walk(top_down=False):
for name in files:
(root / name).unlink()
for name in dirs:
(root / name).rmdir()
Note that as the original question asked about deleting empty directories, this code will also delete files (see also the comment by @DaveSawyer on the accepted answer; and, note that shutil.rmtree
will also delete files beneath the specified root). If you only want to delete empty directories, remove the for name in files:
block and handle the exceptions for non-empty directories if desired.
If, on the other hand, you want to handle files or directories that may be read-only, as done by @acapola, @monir, and @pepoluan you can do so in either for loop.
If you aren't in a position to upgrade to 3.12, you may still be able to utilize os.walk
and pathlib.Path
:
for root, dirs, files in os.walk(dir_to_search, topdown=False):
root_path = Path(root)
for name in files:
(root_path / name).unlink()
for name in dirs:
(root_path / name).rmdir()
Upvotes: 0
Reputation: 1243
Unfortunately, shutil.rmtree
is not enough when some directories are read-only. I ended up with this (tested on Mac-OS):
def rmtree(path):
if not os.path.exists(path):
return
DIR_READ_WRITE = 0o700
FILE_READ_WRITE = 0o600
def remove_readonly(func, path, _):
"Clear the readonly bit and reattempt the removal"
os.chmod(path, FILE_READ_WRITE)
func(path)
# set all directories as read/write
os.chmod(path, DIR_READ_WRITE)
for root, dirs, files in os.walk(path):
for d in dirs:
os.chmod(os.path.join(root, d), DIR_READ_WRITE)
shutil.rmtree(path, onerror=remove_readonly)
Upvotes: 0
Reputation: 12152
Here is a pythonic and recursion-less solution
>>> for e in sorted(p.rglob('**/*'), key=lambda v: v.is_dir()):
... try:
... e.unlink()
... except IsADirectoryError:
... e.rmdir()
The rglob()
gives you all files and directories recursively in the path p
. The sorted()
with its key
argument takes care that the result is ordered by files first and directories at the end. This makes it possible to make all directories empty with deleting their files first.
The try...except...
part prevents you from using cheap if
statements.
Upvotes: 1
Reputation: 4427
The command os.removedirs
is the tool for the job, if you are only looking for a single path to delete, e.g.:
os.removedirs("a/b/c/empty1/empty2/empty3")
will remove empty1/empty2/empty3
, but leave a/b/c (presuming that c has some other contents).
removedirs(name)
removedirs(name)
Super-rmdir; remove a leaf directory and all empty intermediate
ones. Works like rmdir except that, if the leaf directory is
successfully removed, directories corresponding to rightmost path
segments will be pruned away until either the whole path is
consumed or an error occurs. Errors during this latter phase are
ignored -- they generally mean that a directory was not empty.
Upvotes: 3
Reputation: 1454
The default behavior of os.walk()
is to walk from root to leaf. Set topdown=False
in os.walk()
to walk from leaf to root.
Upvotes: 39
Reputation: 400
For Linux users, you can simply run the shell command in a pythonic way
import os
os.system("rm -r /home/user/folder1 /home/user/folder2 ...")
If facing any issue then instead of rm -r
use rm -rf
but remember f will delete the directory forcefully.
Where rm
stands for remove, -r
for recursively and -rf
for recursively + forcefully.
Note: It doesn't matter either the directories are empty or not, they'll get deleted.
Upvotes: -2
Reputation: 1900
Here's my pure pathlib
recursive directory unlinker:
from pathlib import Path
def rmdir(directory):
directory = Path(directory)
for item in directory.iterdir():
if item.is_dir():
rmdir(item)
else:
item.unlink()
directory.rmdir()
rmdir(Path("dir/"))
Upvotes: 45
Reputation: 1660
The command (given by Tomek) can't delete a file, if it is read only. therefore, one can use -
import os, sys
import stat
def del_evenReadonly(action, name, exc):
os.chmod(name, stat.S_IWRITE)
os.remove(name)
if os.path.exists("test/qt_env"):
shutil.rmtree('test/qt_env',onerror=del_evenReadonly)
Upvotes: 6
Reputation: 6780
Here's another pure-pathlib solution, but without recursion:
from pathlib import Path
from typing import Union
def del_empty_dirs(base: Union[Path, str]):
base = Path(base)
for p in sorted(base.glob('**/*'), reverse=True):
if p.is_dir():
p.chmod(0o666)
p.rmdir()
else:
raise RuntimeError(f'{p.parent} is not empty!')
base.rmdir()
Upvotes: 1
Reputation: 4634
Here is a recursive solution:
def clear_folder(dir):
if os.path.exists(dir):
for the_file in os.listdir(dir):
file_path = os.path.join(dir, the_file)
try:
if os.path.isfile(file_path):
os.unlink(file_path)
else:
clear_folder(file_path)
os.rmdir(file_path)
except Exception as e:
print(e)
Upvotes: 1
Reputation: 502
Just for the next guy searching for a micropython solution, this works purely based on os (listdir, remove, rmdir). It is neither complete (especially in errorhandling) nor fancy, it will however work in most circumstances.
def deltree(target):
print("deltree", target)
for d in os.listdir(target):
try:
deltree(target + '/' + d)
except OSError:
os.remove(target + '/' + d)
os.rmdir(target)
Upvotes: 8
Reputation: 117
better to use absolute path and import only the rmtree function
from shutil import rmtree
as this is a large package the above line will only import the required function.
from shutil import rmtree
rmtree('directory-absolute-path')
Upvotes: 8