pathlib与os:文件路径与系统操作

Python 办公自动化专题 · 用Python优雅处理文件路径和系统操作

专题:Python 自动化办公系统学习

关键词:Python, 自动化办公, pathlib, os模块, 文件路径, 目录遍历, 系统操作, Python文件管理

一、pathlib vs os.path:两种路径处理方式的对比

Python提供了两套文件路径操作方案:传统的 os.path 模块(自Python 1.0起存在)和现代的 pathlib 模块(Python 3.4引入,3.6成为正式标准)。两套方案各有优劣,理解它们的设计哲学对于编写可维护的Python代码至关重要。传统 os.path 以字符串为中心,所有路径表示为普通字符串,通过大量函数进行操作;而 pathlib 采用面向对象设计,将路径封装为 Path 对象,提供了更直观的方法调用和运算符重载。

pathlib的设计理念是让路径操作更自然、更可读。使用 Path 对象而非纯字符串,可以利用面向对象的优势:方法串联、属性访问、运算符重载等。例如,使用 / 运算符进行路径拼接,比 os.path.join() 更加直观。此外,pathlib将文件系统的不同实体(纯路径、具体路径)进行了抽象,提供了 PurePath(纯路径,不涉及IO操作)和 Path(具体路径,可执行IO操作)两个层次。

os.path的优势在于兼容性和生态成熟度。大量遗留代码和第三方库仍采用字符串路径,与这些代码交互时 os.path 更为直接。此外,许多标准库函数(如 open()os.listdir())原生接受字符串路径,使用 os.path 无需额外的类型转换。在Python版本方面,如果项目需要兼容Python 3.5及以下版本,os.path 是更安全的选择。

版本演进与选择建议:pathlib在Python 3.4作为试验性特性引入,3.6成为正式特性。从Python 3.6开始,pathlib被广泛认为是路径操作的首选方案。实际项目中,我们推荐以pathlib为主,在需要与字符串路径API交互时使用 str() 转换即可。对于新项目,应优先使用pathlib;对于维护遗留项目,可以在局部使用pathlib逐步重构。

以下代码对比两套方案的操作方式:

import os import os.path from pathlib import Path # os.path方式:使用字符串和独立函数 base_dir = "/home/user/projects" file_path = os.path.join(base_dir, "docs", "readme.txt") print(os.path.dirname(file_path)) # /home/user/projects/docs print(os.path.basename(file_path)) # readme.txt # pathlib方式:使用Path对象和运算符 base = Path("/home/user/projects") file = base / "docs" / "readme.txt" print(file.parent) # /home/user/projects/docs print(file.name) # readme.txt
# 判断文件是否存在 # os.path方式 if os.path.exists(file_path) and os.path.isfile(file_path): print("文件存在") # pathlib方式 if file.exists() and file.is_file(): print("文件存在")
# 获取文件扩展名 # os.path方式 name, ext = os.path.splitext("data.csv") print(ext) # .csv # pathlib方式 p = Path("data.csv") print(p.suffix) # .csv print(p.stem) # data print(p.suffixes) # ['.csv'],支持多扩展名

二、Path对象详解

Path对象是pathlib的核心,它封装了文件系统路径的所有操作。创建Path对象有多种方式:可以直接传入字符串路径、使用类方法获取特殊路径、或者通过当前路径和家目录等起点进行构建。Path对象一旦创建,就可以通过一系列属性和方法对路径进行解析、拼接和转换。

创建Path对象的常见方式包括:直接传入字符串、使用 Path.cwd() 获取当前工作目录、使用 Path.home() 获取用户家目录。Path对象支持正斜杠 / 运算符重载,这是pathlib最受欢迎的设计之一——路径拼接变得像数学运算一样自然。拼接时,如果右侧以 / 开头,则被视为绝对路径拼接,会替换左侧部分。

路径解析属性是Path对象的一大亮点。通过 .parent 获取父目录(可连续使用,如 .parent.parent)、.name 获取最后一级名称、.suffix 获取扩展名、.stem 获取不含扩展名的文件名。这些属性比 os.path 中的对应函数更加直观简洁。此外,.parts 属性将路径拆分为元组,便于逐级处理。

绝对路径与相对路径转换也是常用操作。.resolve() 将相对路径解析为绝对路径并规范化(解析符号链接、消除 ...);.absolute() 简单加上当前工作目录转换为绝对路径但不做规范化;.relative_to() 计算相对于另一路径的相对路径。

from pathlib import Path # 创建Path对象的多种方式 p1 = Path("docs/report.txt") # 相对路径 p2 = Path("/home/user/docs") # 绝对路径 p3 = Path.home() / "projects" # 家目录下 p4 = Path.cwd() # 当前工作目录 print(f"当前目录:{p4}") print(f"家目录:{p3}") # / 运算符路径拼接 config = Path("/etc") / "nginx" / "sites-available" / "default" print(config) # /etc/nginx/sites-available/default
# 路径解析属性详解 p = Path("/home/user/docs/report.txt") print(f"父目录:{p.parent}") # /home/user/docs print(f"祖父目录:{p.parent.parent}") # /home/user print(f"文件名:{p.name}") # report.txt print(f"主文件名:{p.stem}") # report print(f"扩展名:{p.suffix}") # .txt print(f"路径组件:{p.parts}") # ('/', 'home', 'user', 'docs', 'report.txt') # 绝对路径与相对路径转换 relative = Path("docs/../data/report.txt") print(relative.resolve()) # 规范化后的绝对路径 # 计算相对路径 base = Path("/home/user") target = Path("/home/user/docs/file.txt") print(target.relative_to(base)) # docs/file.txt
# 处理带空格的路径 path_with_space = Path("/home/user/My Documents/file.txt") print(path_with_space.as_posix()) # Unix风格斜杠 # Windows路径转换 import sys if sys.platform == "win32": win_path = Path("C:\\Users\\admin\\file.txt") print(win_path.as_posix()) # C:/Users/admin/file.txt print(win_path.drive) # C:

三、文件与目录操作

pathlib将常见的文件与目录操作封装为Path对象的方法,让代码更加简洁一致。相比于 osshutil 模块中分散的函数,pathlib提供了一站式的操作体验:创建目录、读写文件、重命名、删除等操作都可以通过Path对象的方法直接完成。

创建与删除目录.mkdir() 方法创建目录,通过 exist_ok=True 避免目录已存在时抛出异常,parents=True 同时创建所有不存在的父目录(类似 mkdir -p)。.rmdir() 删除空目录,.rmdir() 要求目录为空,否则抛出异常。

文件读写操作是pathlib相比 os.path 的一大优势。.read_text().write_text() 方法可以直接读取和写入文本文件,无需手动管理文件打开和关闭。同样,.read_bytes().write_bytes() 用于二进制文件的读写。这些方法内部自动处理资源释放,代码更加简洁安全。

文件重命名、移动与删除.rename().replace() 重命名或移动文件(replace 在目标存在时会强制覆盖),.unlink() 删除文件。值得注意的是,这些操作对文件和目录都适用,但 .unlink() 用于文件,.rmdir() 仅用于空目录。

from pathlib import Path # 创建目录(类似 mkdir -p) data_dir = Path("project/data/2026") data_dir.mkdir(parents=True, exist_ok=True) print("目录创建成功") # 创建多个子目录 for month in ["01", "02", "03"]: (data_dir / month).mkdir(parents=True, exist_ok=True)
# 文件读写操作 file_path = Path("example.txt") # 写入文本(自动编码处理) file_path.write_text("Hello, Python!\n这是pathlib写入的内容。", encoding="utf-8") # 读取文本 content = file_path.read_text(encoding="utf-8") print(content) # 二进制文件操作 binary_path = Path("image.png") if binary_path.exists(): data = binary_path.read_bytes() print(f"文件大小:{len(data)} 字节") # 追加写入(使用标准open) with file_path.open("a", encoding="utf-8") as f: f.write("这是追加的内容。\n")
# 文件重命名、移动和删除 src = Path("old_report.txt") dst = Path("archive/2026_report.txt") # 先确保目标目录存在 dst.parent.mkdir(parents=True, exist_ok=True) # 重命名/移动文件 src.rename(dst) # 将旧文件移动到新位置并改名 # 强制覆盖移动(推荐使用replace) # src.replace(dst) # 目标存在时覆盖 # 删除文件 if dst.exists(): dst.unlink() # 删除文件 print("文件已删除") # 删除目录(只能删除空目录) empty_dir = Path("temp_dir") if empty_dir.exists(): empty_dir.rmdir() # 目录必须为空

四、路径信息与属性

获取文件或目录的详细信息是文件和系统操作中的常见需求。pathlib通过 .stat() 方法(对应 os.stat())提供了全面的文件元信息查询能力,包括文件大小、修改时间、访问权限等。同时,pathlib还提供了多个便捷的属性方法用于文件类型判断和权限检查。

文件统计信息.stat() 返回一个 os.stat_result 对象,包含文件的所有元数据:.st_size 文件大小(字节)、.st_mtime 最后修改时间(时间戳)、.st_atime 最后访问时间、.st_ctime 创建时间(Windows)或元数据修改时间(Unix)。获取时间戳后可以使用 datetime 模块转换为可读的日期格式。

文件类型判断:pathlib提供了一系列方法来判断路径的类型:.is_file() 是否为普通文件、.is_dir() 是否为目录、.is_symlink() 是否为符号链接、.is_socket() 是否为套接字文件(Unix)、.is_fifo() 是否为命名管道(Unix)。这些方法内部调用 os.stat() 但提供了更友好的接口。

权限检查.stat().st_mode 包含文件的权限信息。pathlib提供了 .owner() 获取文件所有者用户名、.group() 获取文件所属组名。结合权限位掩码可以检查文件的读写执行权限。在自动化办公场景中,这些信息常用于日志文件轮转、备份策略中的文件筛选。

from pathlib import Path import datetime import stat # 获取文件详细信息 file_path = Path("data.txt") if file_path.exists(): info = file_path.stat() print(f"文件大小:{info.st_size} 字节") print(f"占用空间:{info.st_size / 1024:.2f} KB") # 时间戳转可读格式 mtime = datetime.datetime.fromtimestamp(info.st_mtime) print(f"最后修改时间:{mtime.strftime('%Y-%m-%d %H:%M:%S')}") atime = datetime.datetime.fromtimestamp(info.st_atime) print(f"最后访问时间:{atime.strftime('%Y-%m-%d %H:%M:%S')}")
# 文件类型和权限检查 target = Path("target_dir") # 文件类型判断 print(f"是文件吗?{target.is_file()}") print(f"是目录吗?{target.is_dir()}") # 符号链接处理 link = Path("my_link") if link.is_symlink(): print(f"链接目标:{link.readlink()}") # 所有者信息 if target.exists(): print(f"所有者:{target.owner()}") print(f"所属组:{target.group()}") # 权限检查 mode = target.stat().st_mode print(f"是否可读:{bool(mode & stat.S_IRUSR)}") print(f"是否可写:{bool(mode & stat.S_IWUSR)}") print(f"是否可执行:{bool(mode & stat.S_IXUSR)}")
# 批量获取目录中所有文件的信息 from pathlib import Path directory = Path("downloads") if directory.exists() and directory.is_dir(): print("文件名\t\t大小\t\t修改时间") print("-" * 50) for f in directory.iterdir(): if f.is_file(): info = f.stat() mtime = datetime.datetime.fromtimestamp(info.st_mtime) print(f"{f.name}\t\t{info.st_size}\t\t{mtime.strftime('%m-%d %H:%M')}")

五、目录遍历

目录遍历是文件系统操作的核心需求之一。pathlib提供了多种遍历方式,从简单的单层遍历到深度递归的模式匹配,覆盖了从简单到复杂的所有场景。.iterdir() 适用于简单遍历,.glob() 适用于模式匹配,.rglob() 适用于递归搜索。

iterdir遍历.iterdir() 返回目录下所有项目的生成器(不会递归子目录)。遍历结果包含文件和子目录,返回的是Path对象列表。可以在遍历时使用 .is_file().is_dir() 进行过滤。由于返回的是生成器,对于大目录来说内存友好。

glob模式匹配.glob(pattern) 使用Unix shell风格的通配符匹配文件,支持 *(匹配任意多个字符,不包括 /)、?(匹配单个字符)、[abc](字符集匹配)。.rglob(pattern) 是递归版本的glob,会搜索所有子目录。两者都返回生成器,按需产生匹配结果。

文件过滤与目录树:结合各种条件和列表推导式,可以轻松实现复杂的文件过滤逻辑。例如筛选特定时间范围内的文件、按大小过滤文件、组合多个过滤条件等。利用Python的生成器表达式和列表推导式,可以编写出非常简洁而强大的目录遍历代码。

from pathlib import Path # iterdir 基本遍历 data_dir = Path("data") if data_dir.exists(): print("=== data目录下的所有项目 ===") for item in data_dir.iterdir(): type_str = "目录" if item.is_dir() else "文件" print(f"[{type_str}] {item.name}") # 只列出文件 files = [f for f in data_dir.iterdir() if f.is_file()] print(f"共 {len(files)} 个文件")
# glob 模式匹配 print("=== 所有Python文件 ===") for py_file in Path(".").glob("*.py"): print(py_file) # rglob 递归搜索 print("=== 搜索所有CSV文件(递归)===") for csv_file in Path("project").rglob("*.csv"): print(csv_file) # 组合多个模式 print("=== 图片文件 ===") image_patterns = ["*.jpg", "*.png", "*.gif", "*.webp"] for pattern in image_patterns: for img in Path("assets").rglob(pattern): print(img)
# 文件过滤:按日期和大小 import datetime logs_dir = Path("logs") cutoff = datetime.datetime.now() - datetime.timedelta(days=7) # 找出最近7天内修改过的日志文件 recent_logs = [ f for f in logs_dir.rglob("*.log") if f.is_file() and datetime.datetime.fromtimestamp(f.stat().st_mtime) > cutoff ] print(f"最近7天修改的日志文件:{len(recent_logs)} 个") # 找出大于100MB的文件 large_files = [ f for f in Path("data").rglob("*") if f.is_file() and f.stat().st_size > 100 * 1024 * 1024 ] print(f"大文件(>100MB):{len(large_files)} 个")

六、os模块互补

尽管pathlib极大地简化了路径操作,但 os 模块在某些系统级操作方面仍然不可或缺。os 模块提供了与操作系统交互的核心功能,包括进程管理、环境变量、系统信息等,这些是pathlib没有覆盖的领域。在实际开发中,pathlib与os模块经常配合使用,各取所长。

工作目录与环境变量os.getcwd() 获取当前工作目录,os.chdir() 切换工作目录。这两个函数在自动化脚本中经常用到,特别是在需要临时切换工作目录处理文件时。os.environ 提供了环境变量的字典接口,支持读取和设置系统环境变量,这对于配置管理非常重要。需要注意的是,修改 os.environ 只影响当前进程及其子进程。

跨平台系统信息os.name 返回操作系统名称('posix''nt''java'),os.system() 执行系统命令。在自动化办公中,os.system() 可用于执行简单的shell命令,但更推荐使用 subprocess 模块进行复杂命令执行。此外,os.sepos.linesep 提供了平台相关的路径分隔符和换行符。

os.walk目录遍历os.walk() 是一个强大的目录树遍历生成器,返回三元组 (dirpath, dirnames, filenames)。相比pathlib的 rglobos.walk() 提供了更细粒度的控制,如通过修改 dirnames 列表可以动态控制进入哪些子目录(剪枝)。这在需要排除特定目录(如 .gitnode_modules)时特别有用。

import os # 工作目录操作 original_dir = os.getcwd() print(f"当前工作目录:{original_dir}") # 切换目录并执行操作 os.chdir("/tmp") print(f"切换后目录:{os.getcwd()}") # 切回原目录 os.chdir(original_dir) # 环境变量操作 print(f"PATH环境变量:{os.environ.get('PATH', '未设置')[:50]}...") # 设置临时环境变量 os.environ["MY_APP_MODE"] = "development" print(f"MY_APP_MODE:{os.environ.get('MY_APP_MODE')}")
# os.walk 目录遍历(带排除) import os exclude_dirs = {".git", "node_modules", "__pycache__"} print("=== 项目文件结构(排除.git等目录)===") for root, dirs, files in os.walk("my_project"): # 从dirnames中排除特定目录(实现剪枝) dirs[:] = [d for d in dirs if d not in exclude_dirs] level = root.replace("my_project", "").count(os.sep) indent = " " * level print(f"{indent}{os.path.basename(root)}/") sub_indent = " " * (level + 1) for file in files: print(f"{sub_indent}{file}")
# os.system 执行系统命令(配合pathlib使用) import os from pathlib import Path # 使用pathlib创建目录,用os执行命令 output_dir = Path("output") output_dir.mkdir(exist_ok=True) # 执行系统命令(仅限简单命令,复杂命令用subprocess) exit_code = os.system("echo '系统命令执行完毕'") print(f"命令退出码:{exit_code}") # 获取系统信息 print(f"操作系统:{os.name}") print(f"路径分隔符:'{os.sep}'") print(f"换行符:{repr(os.linesep)}")

七、跨平台处理

跨平台兼容性是文件路径操作中最重要的考量之一。Windows使用反斜杠 \ 作为路径分隔符,而Linux和macOS使用正斜杠 /。此外,Windows还有驱动器号(如 C:)和UNC路径等特殊概念。pathlib通过抽象层优雅地处理了这些差异,让开发者编写一次代码即可在多个平台上运行。

路径分隔符差异:pathlib内部始终使用正斜杠 / 作为路径表示,在Windows上会自动转换为反斜杠。使用 / 运算符拼接路径可以确保跨平台正确性。如果需要在代码中硬编码路径,应始终使用正斜杠 /,pathlib会处理平台适配。

PurePath可移植路径PurePathPath 的基类,只提供路径计算的纯操作(如拼接、解析),不执行任何文件系统IO。PurePosixPathPureWindowsPath 分别对应于Unix风格和Windows风格的纯路径操作。在需要处理与当前平台不同的路径格式时(例如在Windows上解析Linux路径),PurePath特别有用。

跨平台注意事项:除了路径分隔符,还需要注意文件系统大小写敏感性(Linux区分大小写,Windows不区分)、特殊字符处理(冒号在Windows文件名中不允许)、路径长度限制(Windows MAX_PATH限制为260字符)、以及权限模型差异。pathlib的 Path 对象在Windows上会自动处理长路径前缀(\\?\)。

from pathlib import Path, PurePosixPath, PureWindowsPath import sys # 跨平台路径拼接(始终使用正斜杠) config = Path("config/settings.json") print(config) # 当前平台格式:Linux下为config/settings.json # 在任意平台上处理不同风格的路径 unix_path = PurePosixPath("/home/user/docs/file.txt") win_path = PureWindowsPath("C:\\Users\\admin\\docs\\file.txt") print(f"Unix路径各部分:{unix_path.parts}") print(f"Windows驱动器:{win_path.drive}") print(f"Windows路径各部分:{win_path.parts}")
# 跨平台兼容的路径处理 def get_data_path(filename: str) -> Path: """返回数据文件路径,兼容所有平台""" base = Path(__file__).parent.resolve() return base / "data" / filename # 使用 data_file = get_data_path("report.csv") print(f"数据文件路径:{data_file}") # 路径比较(Windows上不区分大小写) p1 = Path("README.TXT") p2 = Path("readme.txt") if sys.platform == "win32": print(p1 == p2) # True(Windows不区分大小写) else: print(p1 == p2) # False(Linux区分大小写)
# 路径格式转换与网络路径 from pathlib import PureWindowsPath # Windows网络路径(UNC) unc = PureWindowsPath("\\\\server\\share\\folder\\file.txt") print(f"UNC服务器:{unc.parts[0]}") # Windows路径转Unix风格 win_path = PureWindowsPath("C:\\Users\\admin") print(win_path.as_posix()) # C:/Users/admin # 判断路径是否为绝对路径 print(PurePosixPath("/etc").is_absolute()) # True print(PureWindowsPath("C:/Windows").is_absolute()) # True print(PureWindowsPath("rel/folder").is_absolute()) # False

八、最佳实践

在文件路径与系统操作方面,遵循一些最佳实践可以避免常见的陷阱,编写出更健壮、更可维护的代码。以下是在实际项目中积累的路径操作经验和注意事项。

路径异常处理:文件系统操作天然容易出错——文件不存在、权限不足、磁盘已满、路径非法等。使用pathlib时,应当对关键操作进行异常捕获。常见的异常包括 FileNotFoundError(文件不存在)、PermissionError(权限不足)、FileExistsError(文件已存在)、NotADirectoryError(不是目录)和 OSError(其他系统错误)。合理的异常处理策略是:预期可能失败的操作用try/except包裹,非关键错误给出警告而非崩溃。

资源清理与上下文管理:使用pathlib的文件读写方法(如 read_text())自动管理资源释放。对于更复杂的文件操作,使用 with 语句确保资源正确清理。当创建临时文件或目录时,务必在完成后清理,可以使用 tempfile 模块中的 TemporaryDirectory 上下文管理器自动清理。

编码问题:在不同操作系统上,文件名的编码方式可能不同(Linux使用UTF-8,Windows使用系统代码页)。使用pathlib的 read_text()write_text() 时始终指定 encoding="utf-8"。在处理用户提供的文件名时,注意处理特殊字符(如 /\0: 等)。路径缓存:在需要多次访问同一路径信息时(如多次调用 .stat()),将结果存储在变量中避免重复系统调用。

from pathlib import Path # 稳健的路径操作模式 def safe_read_file(file_path: Path) -> str: """安全读取文件内容,处理各种异常""" try: if not file_path.exists(): return "" if not file_path.is_file(): raise ValueError(f"{file_path} 不是文件") return file_path.read_text(encoding="utf-8") except PermissionError: print(f"警告:无权限读取 {file_path}") return "" except OSError as e: print(f"读取文件失败:{e}") return "" # 使用示例 content = safe_read_file(Path("config.json"))
# 临时目录和资源清理 import tempfile from pathlib import Path # 使用上下文管理器自动清理临时目录 with tempfile.TemporaryDirectory(prefix="my_app_") as tmp_dir: tmp_path = Path(tmp_dir) # 在临时目录中工作 work_file = tmp_path / "temp_data.txt" work_file.write_text("临时数据", encoding="utf-8") print(f"临时文件已创建:{work_file}") print(f"内容:{work_file.read_text(encoding='utf-8')}") # 退出with块后,临时目录自动清理 print("临时目录已清理") # 路径缓存示例 def get_dir_info(directory: Path) -> dict: """获取目录信息,使用变量缓存stat结果""" if not directory.exists(): return {} info = directory.stat() # 只调用一次stat return { "size": info.st_size, "modified": info.st_mtime, "created": info.st_ctime, }
# 编码处理和安全文件名 import re def safe_filename(name: str) -> str: """移除去掉不适合用作文件名的字符""" # 替换Windows和Linux中不合法的文件名字符 return re.sub(r'[<>:"/\\|?*]', '_', name) # 使用Path处理各种编码场景 title = "数据报表: 2026/05/05 (季度)" safe_name = safe_filename(title) file_path = Path("reports") / f"{safe_name}.xlsx" file_path.parent.mkdir(exist_ok=True) print(f"安全文件名:{file_path}")

九、实战案例

将前面学到的知识综合运用,通过三个完整的实战案例展示pathlib和os模块在实际项目中的应用。每个案例都涵盖了路径操作、目录遍历、文件处理等核心技能,是日常自动化工作中最常见的使用场景。

案例一:项目文件结构分析。编写一个工具函数,扫描项目目录并生成文件结构的统计报告。该工具需要递归遍历目录,统计各类文件的数量、总大小、最近修改时间等,并以格式化的方式输出结果。在遍历过程中,需要排除 .gitnode_modules__pycache__ 等非项目文件目录。这个工具在项目管理和代码审查中非常实用。

案例二:日志文件归档。服务器日志文件通常按日增长,需要定期归档清理。实现一个日志归档脚本:查找指定目录下超过30天的旧日志文件,将它们移动到归档目录并压缩(使用gzip),然后删除原文件。脚本需要记录归档操作日志,并在遇到权限问题或文件锁定等异常时优雅处理。

案例三:临时文件夹管理。自动化脚本常常需要创建临时文件夹存放中间结果。设计一个智能临时文件夹管理器,使用 tempfile 创建临时目录,将中间文件写入其中,在任务完成时确保所有临时文件被清理。同时支持异常情况下的清理(使用try/finally或上下文管理器)。

# 案例一:项目文件结构分析 from pathlib import Path import datetime def analyze_project(project_dir: str): """分析项目文件结构并生成统计报告""" base = Path(project_dir).resolve() exclude = {".git", "node_modules", "__pycache__", ".idea", "venv", ".DS_Store"} stats = {"total_files": 0, "total_dirs": 0, "total_size": 0, "by_ext": {}} newest = None for item in base.rglob("*"): if any(p in exclude for p in item.parts): continue if item.is_file(): stats["total_files"] += 1 size = item.stat().st_size stats["total_size"] += size ext = item.suffix or "(无扩展名)" stats["by_ext"][ext] = stats["by_ext"].get(ext, 0) + 1 mtime = item.stat().st_mtime if newest is None or mtime > newest[1]: newest = (item, mtime) elif item.is_dir(): stats["total_dirs"] += 1 # 输出报告 print("=" * 50) print(f"项目分析报告:{base}") print("=" * 50) print(f"目录数:{stats['total_dirs']}") print(f"文件数:{stats['total_files']}") print(f"总大小:{stats['total_size'] / 1024:.2f} KB") print("\n文件类型分布:") for ext, count in sorted(stats["by_ext"].items()): print(f" {ext:>12} : {count} 个") if newest: mtime_str = datetime.datetime.fromtimestamp(newest[1]).strftime("%Y-%m-%d %H:%M") print(f"\n最近修改:{newest[0].name} ({mtime_str})") # 使用示例 analyze_project(".")
# 案例二:日志文件归档 from pathlib import Path import datetime import gzip import shutil def archive_old_logs(log_dir: str, days_old: int=30, archive_dir: str=None): """归档超过指定天数的日志文件""" log_path = Path(log_dir) if not log_path.exists(): print(f"日志目录不存在:{log_dir}") return archive_path = Path(archive_dir or log_path / "archive") archive_path.mkdir(parents=True, exist_ok=True) cutoff = datetime.datetime.now() - datetime.timedelta(days=days_old) archived_count = 0 for log_file in log_path.glob("*.log"): mtime = datetime.datetime.fromtimestamp(log_file.stat().st_mtime) if mtime < cutoff: try: # 压缩并归档 archive_name = archive_path / f"{log_file.stem}_{mtime.strftime('%Y%m%d')}.log.gz" with log_file.open("rb") as f_in: with gzip.open(archive_name, "wb") as f_out: shutil.copyfileobj(f_in, f_out) # 删除原文件 log_file.unlink() archived_count += 1 print(f"已归档:{log_file.name} -> {archive_name.name}") except (PermissionError, OSError) as e: print(f"归档失败 {log_file.name}:{e}") print(f"\n归档完成,共处理 {archived_count} 个文件") # 使用示例 # archive_old_logs("/var/log/myapp", days_old=30)
# 案例三:临时文件夹管理器 from pathlib import Path import tempfile import shutil class TempWorkspace: """安全的临时工作目录管理器""" def __init__(self, prefix: str="workspace_"): self.prefix = prefix self.path = None def __enter__(self): self.path = Path(tempfile.mkdtemp(prefix=self.prefix)) print(f"创建临时目录:{self.path}") return self.path def __exit__(self, exc_type, exc_val, exc_tb): if self.path and self.path.exists(): shutil.rmtree(self.path, ignore_errors=True) print(f"清理临时目录:{self.path}") # 使用临时工作区处理文件 def process_files(input_files): """在临时工作区中处理文件""" with TempWorkspace(prefix="file_process_") as work_dir: print("正在处理文件...") # 复制输入文件到工作区 for f in input_files: src = Path(f) if src.exists(): shutil.copy2(src, work_dir / src.name) # 在工作区中进行处理 for item in work_dir.iterdir(): if item.is_file(): content = item.read_text(encoding="utf-8") item.write_text(content.upper(), encoding="utf-8") print("处理完成!结果保存在临时目录中") return work_dir # 退出with块后,临时目录自动被清理 # 不需要手动删除