☰
Python文件和目录操作详解
2026/10/11 10:50:19 网站建设 项目流程

前言


文件操作大家讲得多,目录操作反而常被忽略,结果一到真实项目就卡壳:日志目录不存在、输出目录已存在、临时文件忘了删、递归遍历把隐藏目录也扫了进来。这些场景都属于「目录」这一层的操作。


一个典型误解是「建目录就用os.mkdir」。os.mkdir只能建一层,父目录不存在照样报错;要逐级创建得用os.makedirs。类似地,「删目录」也分三种:os.rmdir只删空目录,os.removedirs自底向上删空链,shutil.rmtree才删整棵树。


本文围绕目录展开:创建与删除、用pathlib做遍历和筛选、临时目录的正确用法,最后给一个「安全清理过期文件」的实战脚本。示例面向 Python 3.8+,用到更高版本特性时会注明。


一、创建、删除与重命名目录


os这一侧的接口:


# 适用于 Python 3.8+
import os

os.mkdir("out") # 建一层;父目录不在就抛 FileNotFoundError
os.makedirs("logs/2026/10", exist_ok=True) # 逐级建;exist_ok=True 已存在不报错
os.rmdir("out") # 只能删空目录
os.removedirs("a/b/c") # 自底向上删空目录,遇到非空就停

os.makedirs的签名是os.makedirs(name, mode=0o777, exist_ok=False)。注意exist_ok只对「目标目录已存在」放行,如果目标存在但是文件,照样抛错。mode决定权限位,但会被进程的 umask 修正,所以实际权限可能比0o777更严。


pathlib一侧:


# 适用于 Python 3.8+
from pathlib import Path

p = Path("logs") / "2026" / "10"
p.mkdir(parents=True, exist_ok=True)

q = Path("out")
q.rmdir() # 同样只能删空目录

Path.mkdir(parents=True, exist_ok=True)这一个方法就覆盖了os.mkdir和os.makedirs两种需求,这是pathlib比较顺手的地方。


重命名目录用os.rename/os.replace,pathlib里是Path.rename()/Path.replace():




操作os 写法pathlib 写法



建一级目录os.mkdir(p)Path(p).mkdir()

建多级目录os.makedirs(p, exist_ok=True)Path(p).mkdir(parents=True, exist_ok=True)

删空目录os.rmdir(p)Path(p).rmdir()

删整树shutil.rmtree(p)shutil.rmtree(p)

改名os.rename(src, dst)Path(src).rename(dst)

覆盖式改名os.replace(src, dst)Path(src).replace(dst)



二、用 pathlib 遍历与筛选目录


Path.iterdir()相当于os.listdir(),但返回的是Path对象而不是字符串,省掉反复拼接:


# 适用于 Python 3.8+
from pathlib import Path

for entry in Path(".").iterdir():
kind = "目录" if entry.is_dir() else "文件"
print(kind, entry.name)

要做模式匹配,用Path.glob()(当前层)和Path.rglob()(递归):


# 适用于 Python 3.8+
from pathlib import Path

# 当前目录下所有 .csv
for p in Path("data").glob("*.csv"):
print(p)

# 递归找所有 .py,等价于 glob("**/*.py")
for p in Path(".").rglob("*.py"):
print(p)

glob和rglob返回的都是生成器,不会一次性把所有匹配项算出来。若路径不存在,glob只是产出空结果,不报错——这既是优点也容易掩盖「目录写错了」这种低级问题。


筛选时另一个常见需求是「只看文件、跳过隐藏项」:


# 适用于 Python 3.8+
from pathlib import Path

files = [p for p in Path(".").iterdir()
if p.is_file() and not p.name.startswith(".")]
print(len(files), "个可见文件")

注意Path.iterdir()返回的条目顺序取决于文件系统,需要稳定顺序一定要自己sorted()。


三、临时文件与临时目录


tempfile模块负责「用完就删」的临时资源。首选上下文管理器写法,退出时自动清理:


# 适用于 Python 3.8+
import tempfile
from pathlib import Path

with tempfile.TemporaryDirectory() as tmp:
work = Path(tmp) / "stage.txt"
work.write_text("临时内容", encoding="utf-8")
print("工作目录:", tmp)
# 离开 with 后,整个目录连同内容一起被删除

需要临时文件并且要让别的程序也能按名字打开时,用NamedTemporaryFile:


# 适用于 Python 3.8+
import tempfile

with tempfile.NamedTemporaryFile(mode="w", suffix=".txt",
encoding="utf-8", delete=True) as fp:
fp.write("hello")
fp.flush() # 让内容真正落盘,别人才读得到
print(fp.name)

几个准确的事实:NamedTemporaryFile的签名是NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, delete=True, *, errors=None, delete_on_close=True);delete_on_close是Python 3.12才加入的参数,在此之前只有delete;TemporaryDirectory的delete参数同样是 3.12 加入的。写兼容 3.8~3.11 的代码时不要用这两个参数。


底层接口tempfile.mkdtemp(suffix=None, prefix=None, dir=None)直接建一个临时目录并返回它的路径,但不会自动删除,清理责任在使用者身上:


# 适用于 Python 3.8+
import shutil
import tempfile

d = tempfile.mkdtemp(prefix="build_")
try:
print("临时目录:", d)
finally:
shutil.rmtree(d, ignore_errors=True)

tempfile.gettempdir()返回系统用来放临时文件的目录;TemporaryDirectory()不传dir时就落在这里。


实战:清理超过 N 天的日志文件


下面脚本演示「目录创建 + 递归筛选 + 文件信息 + 删除」的组合,重点是删除前先确认目标是文件:


# 适用于 Python 3.8+
import time
from pathlib import Path

def clean_logs(root="logs", days=7, dry_run=True):
root = Path(root)
if not root.is_dir():
raise NotADirectoryError(f"{root} 不是目录")

deadline = time.time() - days * 86400
removed = 0
for path in root.rglob("*.log"):
if not path.is_file():
continue
if path.stat().st_mtime < deadline:
print(("将删除 " if dry_run else "删除 ") + str(path))
if not dry_run:
path.unlink()
removed += 1
return removed

if __name__ == "__main__":
print("命中", clean_logs(), "个文件(预演模式)")

脚本默认dry_run=True,先打印再决定是否真删,这是处理删除逻辑的稳妥做法。path.unlink()只删文件,遇到目录会抛IsADirectoryError,正好当作一道保险。


常见坑点


1. 用os.mkdir建多级目录


❌os.mkdir("a/b/c"),a不存在时直接抛FileNotFoundError。 ✅os.makedirs("a/b/c", exist_ok=True)或Path("a/b/c").mkdir(parents=True, exist_ok=True)。


2. 以为rmdir能删非空目录


❌os.rmdir("build")里还有文件,报OSError: Directory not empty。 ✅ 明确要整树删除时用shutil.rmtree("build"),并二次确认路径。


3. 重复执行脚本被FileExistsError打断


❌Path("out").mkdir()第二次运行报错。 ✅ 加exist_ok=True。


4. 遍历目录时顺序不确定


❌ 假设iterdir()返回「按名字排好序」的列表,用它做分页或对比。 ✅sorted(Path(".").iterdir()),或sorted(p.name for p in ...)。


5. 临时目录不用上下文管理器


❌tempfile.mkdtemp()之后忘了删,临时目录越堆越多。 ✅mkdtemp用try/finally配shutil.rmtree,或直接用TemporaryDirectory()。


6.glob匹配不到就以为代码错了


❌Path("data").glob("*.csv")因为data不存在而静默返回空,误判成「没有数据」。 ✅ 匹配前先Path("data").is_dir(),路径不对就尽早报错。


7. Windows 上删除仍被占用的文件


❌shutil.rmtree(tmp)报PermissionError,因为里面的文件还开着。 ✅ 先确保文件句柄已关闭;必要时用TemporaryDirectory(ignore_cleanup_errors=True)。


8. 递归遍历撞上符号链接成环


❌rglob在存在循环符号链接的目录上可能反复进出。 ✅ 检查path.is_symlink()并跳过,或改用不跟随链接的遍历方式。


总结




场景推荐写法注意



建多级目录Path(p).mkdir(parents=True, exist_ok=True)目标为文件时仍报错

删空目录os.rmdir/Path.rmdir非空会失败

删整树shutil.rmtree危险,先确认路径

列目录Path.iterdir()顺序不保证

模式匹配Path.glob/Path.rglob路径不存在时静默为空

临时目录tempfile.TemporaryDirectory()自动清理

临时文件tempfile.NamedTemporaryFile()需要落盘时先flush()



目录操作的难点不在 API,而在「已存在」「非空」「被占用」「路径写错」这几种边界。把exist_ok用起来、把删除做成可选、把路径先判断再使用,脚本的健壮性会立刻上一个台阶。




需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询