Python 刪除目錄內容需區分檔案與子目錄,並處理權限、空目錄、符號連結等邊界情況。本文提供健壯的刪除函式,並對比 os、shutil、pathlib 三種主流做法。
import os
import shutil
def clear_directory(root_dir):
"""刪除目錄下所有內容,保留目錄本身"""
if not os.path.isdir(root_dir):
raise ValueError("非目錄: %s" % root_dir)
for name in os.listdir(root_dir):
path = os.path.join(root_dir, name)
try:
if os.path.isfile(path) or os.path.islink(path):
os.remove(path) # 檔案/符號連結
print("已刪除檔案: %s" % path)
elif os.path.isdir(path):
shutil.rmtree(path) # 目錄樹 (含內容)
print("已刪除目錄: %s" % path)
except Exception as e:
print("刪除失敗 %s: %s" % (path, e))
# 使用
clear_directory("/home/user/temp")
from pathlib import Path
def clear_directory_modern(root_dir):
root = Path(root_dir)
if not root.is_dir():
raise ValueError("非目錄")
for item in root.iterdir():
try:
if item.is_file() or item.is_symlink():
item.unlink() # 刪除檔案/連結
print("刪除檔案:", item)
elif item.is_dir():
shutil.rmtree(str(item)) # pathlib 無內建 rmtree
print("刪除目錄:", item)
except Exception as e:
print("失敗:", item, e)
shutil.rmtree(path, ignore_errors=False, onerror=None)
# ignore_errors=True: 忽略所有錯誤,靜默繼續
shutil.rmtree(path, ignore_errors=True)
# onerror: 自訂錯誤處理函式 func(path, exc_info)
def handle_remove_error(func, path, exc_info):
import stat
# 嘗試修正唯讀屬性後重試
if not os.access(path, os.W_OK):
os.chmod(path, stat.S_IWUSR)
func(path)
else:
raise
shutil.rmtree(path, onerror=handle_remove_error)
| 需求 | 方法 |
|---|---|
| 刪除目錄及所有內容 | shutil.rmtree(dir_path) |
| 僅清空內容,保留目錄 | 遍歷 os.listdir 逐一刪除 |
| 刪除空目錄 | os.rmdir(path) / Path.rmdir() |
ls -l),必要時 sudo 或 chmod。os.chmod(path, stat.S_IWRITE) 後再刪除。os.scandir() (Python 3.5+) 替代 os.listdir(),減少 stat 系統呼叫。
from concurrent.futures import ThreadPoolExecutor
def parallel_clear(root_dir, max_workers=8):
with ThreadPoolExecutor(max_workers) as executor:
futures = []
for name in os.listdir(root_dir):
path = os.path.join(root_dir, name)
if os.path.isfile(path):
futures.append(executor.submit(os.remove, path))
elif os.path.isdir(path):
futures.append(executor.submit(shutil.rmtree, path))
for f in futures:
f.result() # 等待完成並傳播異常
PROTECTED_PATHS = {"/", "/home", "/etc", "/var", "/usr", "C:\\", "C:\\Windows"}
def safe_clear(path):
abs_path = os.path.abspath(path)
for protected in PROTECTED_PATHS:
if abs_path.startswith(os.path.abspath(protected)):
raise SecurityError("拒絕刪除受保護目錄: %s" % abs_path)
clear_directory(abs_path)