操作文件和目录
跨平台写 Python 脚本时,最常踩的坑就是硬编码路径分隔符、适配系统命令,或者根本不知道当前代码跑在哪个操作系统上。这时候,os 家族(含 os.path)、shutil、还有 Python 3.4 起就超好用的 pathlib,就是你的救星!
这篇文章会带你梳理 Python 里文件和目录操作的核心工具链,顺便给出一堆安全提示和实用案例,让你写出的脚本清晰可靠,还能丝滑运行在 Windows / Linux / macOS 上。
操作系统类型检测
先搞清楚脚本的运行环境,才能做针对性适配——比如路径处理、调用系统命令等。
import os
# 获取最基础的系统标识
print(os.name) # 输出结果只有两种
💡 常见标识对应的系统:
posix:Linux、macOS、BSD 等 Unix-like 系统
nt:Windows 系统
如果需要获取更详细的内核、主机名等信息(仅支持 Unix-like 系统),可以这样:
if os.name == 'posix':
print(os.uname())
# 输出示例:posix.uname_result(sysname='Linux', nodename='ubuntu-server',
# release='6.5.0', version='#1 SMP PREEMPT_DYNAMIC', machine='x86_64')
⚠️ 注意:Windows 上直接调用 os.uname() 会报错,一定要先判断 os.name。
环境变量操作
所有系统环境变量都存储在类字典结构 os.environ 里。
虽然它可以直接赋值,但强烈推荐用 .get() 方法,避免因变量不存在而抛出 KeyError。
# ⚠️ 生产环境中不要随便打印所有环境变量,可能会泄露敏感信息!
# print(os.environ)
# 获取已知变量(推荐)
python_path = os.environ.get('PYTHONPATH')
# 获取不存在的变量,并指定默认值
default_config = os.environ.get('APP_CONFIG_PATH', '/etc/app/default.conf')
这样写不仅更安全,还让你能优雅地给出回退方案。
文件与目录操作
核心原则💡
永远不要手动拼接路径字符串(例如 '/Users/xxx' + 'test.txt')。
不同系统的分隔符不一样——Windows 用 \,Unix-like 用 /。手动拼接会破坏跨平台兼容性,让脚本悄无声息地崩掉。
下面介绍两个路径工具:经典但兼容性极好的 os.path,以及更现代、更优雅的 pathlib。
经典路径工具:os.path
虽然现在更推荐 pathlib,但 os.path 兼容性最好(Python 2.x/3.x 通用),很多老项目还在用。掌握它依然很有必要:
# 1. 获取绝对路径
current_dir = os.path.abspath('.')
print(current_dir) # 输出类似 /home/xxx/Desktop 或 C:\Users\xxx\Desktop
# 2. 跨平台路径拼接(自动适配分隔符)
full_path = os.path.join('/Users/xxx', 'docs', 'python', 'intro.txt')
print(full_path) # Unix-like: /Users/xxx/docs/python/intro.txt;Windows 自动变为反斜杠
# 3. 拆分路径(目录 + 文件名/目录名)
parent_dir, last_part = os.path.split('/Users/xxx/docs/intro.txt')
print(f"父目录:{parent_dir},最后部分:{last_part}")
# 4. 拆分文件名(主名 + 扩展名)
root_name, ext = os.path.splitext('intro.txt')
print(f"主名:{root_name},扩展名:{ext}")
# 5. 常见属性检查
print(os.path.exists('test.txt')) # True/False
print(os.path.isfile('test.txt')) # True/False
print(os.path.isdir('my_project')) # True/False
现代路径工具:pathlib(Python 3.4+ 首选)
pathlib 用面向对象的方式处理路径,把路径变成对象而不是字符串,代码更简洁、可读性更高,堪称 os.path 的全面替代品。
from pathlib import Path
# 1. 初始化路径对象
p = Path('.') # 当前目录
full_p = Path('/Users/xxx/docs/python/intro.txt')
# 2. 路径操作(链式调用,一气呵成)
parent_dir = full_p.parent
last_part = full_p.name
root_name = full_p.stem
ext = full_p.suffix
# 3. 跨平台拼接(用 / 操作符,超级直观!)
new_p = Path('/Users/xxx') / 'docs' / 'python' / 'intro.txt'
# 4. 常见属性检查
print(new_p.exists())
print(new_p.is_file())
print(new_p.is_dir())
# 5. 快速遍历
py_files = list(p.glob('*.py')) # 当前目录下的所有 .py 文件
all_py_files = list(p.rglob('*.py')) # 递归遍历所有子目录里的 .py 文件
对新手来说,pathlib 的语法更像自然语言,推荐在新的 Python 项目里直接使用。
目录操作
不管用 os 还是 pathlib,底层逻辑差不多,但语法有区别。这里把两种方式放在一起,方便对比:
用 os 模块
# 1. 创建单级目录(已存在会报错)
os.mkdir('single_dir')
# 2. 创建多级目录(Python 3.2+ 推荐,加 exist_ok=True 已存在不报错)
os.makedirs('multi/level/dir', exist_ok=True)
# 3. 删除**空目录**(非空会报错)
os.rmdir('single_dir')
# 4. 列出目录下所有内容(不含子目录内容)
print(os.listdir('.'))
用 pathlib
p = Path('.')
# 1. 创建单级/多级目录(parents=True 自动创建父目录,exist_ok=True 已存在不报错)
(p / 'multi/level/dir').mkdir(parents=True, exist_ok=True)
# 2. 删除空目录
(p / 'single_dir').rmdir()
# 3. 列出目录下所有内容(返回 Path 对象列表,更方便后续操作)
print(list(p.iterdir()))
文件操作
用 os 模块
# 1. 重命名/移动文件(也可移动目录,但空目录推荐用 rmdir)
os.rename('old.txt', 'new.txt')
os.rename('src_dir/file.txt', 'dst_dir/file.txt')
# 2. 删除文件(删除目录会报错)
os.remove('file_to_delete.txt')
用 pathlib
old_p = Path('old.txt')
new_p = Path('new.txt')
# 1. 重命名/移动文件
old_p.rename(new_p)
# 2. 移动时如果目标目录还不存在,可以先创建
# (new_p.parent).mkdir(parents=True, exist_ok=True)
# old_p.rename(new_p)
# 3. 删除文件(Python 3.8+ 加 missing_ok=True,不存在也不报错)
old_p.unlink(missing_ok=True)
高级文件操作
os / pathlib 能覆盖基础需求,但像复制文件、递归删除、移动大目录这类进阶操作,最好交给 shutil 模块(“shell utilities”的缩写)。它封装了很多高效、安全的文件操作,比你自己手动实现靠谱得多。
import shutil
# 1. 复制文件(推荐 copy2,保留修改时间、权限等元数据)
shutil.copy2('source.txt', 'destination.txt')
shutil.copy2('source.txt', 'backup_dir/') # 复制到指定目录
# 2. 递归复制整个目录(目标目录不能已存在!)
shutil.copytree('src_project', 'dst_project_backup')
# 3. 递归删除目录(⚠️ 超级危险!没有回收站!操作前务必确认!)
# shutil.rmtree('dir_to_delete')
# 4. 移动文件/目录(自动判断是复制+删除还是直接移动)
shutil.move('src', 'dst')
使用 shutil.rmtree 之前一定要确保变量名正确,最好加上二次确认,误删毁一生。
实用小技巧
1. 列出当前目录下的所有子目录
# os.path 版本
subdirs = [d for d in os.listdir('.') if os.path.isdir(d)]
# pathlib 版本(更直观)
subdirs = [d for d in Path('.').iterdir() if d.is_dir()]
2. 递归查找文件
比如找出当前目录和所有子目录里,文件名包含 log 的文件:
# os.walk 版本(Python 2.x/3.x 通用)
def find_logs(root='.'):
for root_dir, _, files in os.walk(root):
for f in files:
if 'log' in f:
print(os.path.relpath(os.path.join(root_dir, f)))
# pathlib 版本(更简洁)
def find_logs(root='.'):
for log_file in Path(root).rglob('*log*'):
if log_file.is_file():
print(log_file.relative_to(root))
3. 计算目录总大小
# os.walk 版本
def get_dir_size(root='.'):
total = 0
for dirpath, _, filenames in os.walk(root):
for f in filenames:
fp = os.path.join(dirpath, f)
# 跳过可能已不存在的临时文件,避免 os.path.getsize 报错
if os.path.isfile(fp):
total += os.path.getsize(fp)
return total
# 一个简单的大小格式化工具(可选)
def format_size(size):
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if size < 1024.0:
return f"{size:.2f} {unit}"
size /= 1024.0
总结
- 环境检测:用
os.name 快速判断系统,Unix-like 下可以进一步用 os.uname() 获取详细信息。
- 路径处理:Python 3.4+ 首选
pathlib(面向对象、支持 / 操作符、链式调用),老项目或需兼容 Python 2 时用 os.path。
- 基础目录/文件操作:使用
os 或 pathlib 中的对应方法,注意跨平台和exception-handling。
- 复杂操作:复制、递归删除、移动等交给
shutil,功能强大且安全封装。
- 安全提醒:
- 生产环境不要轻易打印
os.environ 的全部内容。
- 删除操作(尤其是
rmtree)之前一定要做好判断或二次确认。
- 操作前尽量用
.exists()、.is_file()、.is_dir() 检查状态,避免无谓异常。
掌握了这套工具链,无论是日常脚本还是项目代码,你都能写出既简洁又健壮的跨平台文件操作逻辑。