Python os 模块:文件和目录操作指南
Updated on
每个读取配置文件、保存输出、组织数据或自动化部署的 Python 脚本都需要与文件系统交互。但文件路径在 Windows、macOS 和 Linux 上的行为不同。硬编码的 /home/user/data 路径在 Windows 上无法工作。使用 + 和 / 手动拼接字符串会导致双斜杠 bug 和缺失的分隔符。而且没有适当的检查,你的脚本可能会删除错误的文件或在目录已存在时崩溃。
Python 的 os 模块解决了这些问题。它提供了一个可移植的跨平台接口来与操作系统交互——创建目录、列出文件、读取环境变量、操作路径和遍历目录树。它随每个 Python 安装一起提供,不需要 pip install,并自动处理平台差异。
本指南涵盖了 os 模块中的每个重要函数,按用例组织,配有可以直接复制到项目中的实用示例。
获取当前工作目录
在对文件进行任何操作之前,你需要知道脚本在哪里运行。os.getcwd() 以绝对路径的形式返回当前工作目录。
import os
# 获取当前工作目录
cwd = os.getcwd()
print(cwd) # e.g., /home/user/projects/myapp
# 更改工作目录
os.chdir('/tmp')
print(os.getcwd()) # /tmp
# 切换回去
os.chdir(cwd)在构建相对路径或需要在临时更改后恢复工作目录时使用 os.getcwd()。
目录操作
创建目录
os.mkdir() 创建单个目录。如果目录已存在会引发 FileExistsError,如果父目录不存在会引发 FileNotFoundError。
import os
# 创建单个目录
os.mkdir('output')
# 仅在不存在时创建
if not os.path.exists('output'):
os.mkdir('output')os.makedirs() 递归创建目录——包括所有缺失的父目录。exist_ok=True 参数可以防止目录已存在时的错误。
import os
# 一次调用创建嵌套目录
os.makedirs('data/raw/2026/january', exist_ok=True)
# 没有 exist_ok,如果 'data' 已存在会引发 FileExistsError
# os.makedirs('data/raw/2026/january') # 可能引发错误列出目录内容
os.listdir() 返回给定路径中所有条目(文件和目录)的列表。
import os
# 列出当前目录中的所有内容
entries = os.listdir('.')
print(entries) # ['main.py', 'data', 'output', 'README.md']
# 列出特定目录的内容
data_files = os.listdir('/var/log')
print(data_files)os.scandir() 是一个更高效的替代方案,返回带有缓存文件属性的 DirEntry 对象。当你需要文件元数据和名称时使用它。
import os
with os.scandir('.') as entries:
for entry in entries:
info = entry.stat()
print(f"{entry.name:30s} {'DIR' if entry.is_dir() else 'FILE':4s} {info.st_size} bytes")删除目录
os.rmdir() 删除空目录。对于非空目录,使用 shutil.rmtree()。
import os
import shutil
# 删除空目录
os.rmdir('output')
# 删除目录及其所有内容(谨慎使用)
shutil.rmtree('data/raw/2026')文件操作
删除文件
os.remove()(或其别名 os.unlink())删除单个文件。如果文件不存在会引发 FileNotFoundError。
import os
# 删除文件
os.remove('temp_output.csv')
# 带存在性检查的安全删除
filepath = 'temp_output.csv'
if os.path.exists(filepath):
os.remove(filepath)
print(f"已删除 {filepath}")
else:
print(f"{filepath} 未找到")重命名和移动文件
os.rename() 重命名或移动文件或目录。如果目标已存在,行为取决于平台——在 Unix 上可能会覆盖,但在 Windows 上可能会引发错误。使用 os.replace() 进行有保证的原子替换。
import os
# 重命名文件
os.rename('old_report.csv', 'new_report.csv')
# 将文件移动到不同目录
os.rename('report.csv', 'archive/report_2026.csv')
# 原子替换(在所有平台上覆盖目标)
os.replace('new_data.csv', 'data.csv')获取文件信息
os.stat() 返回详细的文件元数据,包括大小、权限和时间戳。
import os
from datetime import datetime
info = os.stat('data.csv')
print(f"大小: {info.st_size} bytes")
print(f"修改时间: {datetime.fromtimestamp(info.st_mtime)}")
print(f"创建时间: {datetime.fromtimestamp(info.st_ctime)}")
print(f"权限: {oct(info.st_mode)}")使用 os.path 进行路径操作
os.path 子模块是大部分日常文件系统工作发生的地方。它跨平台处理路径构建、验证和分解。
安全构建路径
永远不要用 + 拼接路径。使用 os.path.join() 来正确构建路径,无论操作系统如何。
import os
# 正确:os.path.join 处理分隔符
path = os.path.join('data', 'raw', 'sales.csv')
print(path) # Unix 上 'data/raw/sales.csv',Windows 上 'data\\raw\\sales.csv'
# 错误:字符串拼接可能出错
bad_path = 'data' + '/' + 'raw' + '/' + 'sales.csv' # 在 Windows 上出错检查存在性和类型
import os
# 检查路径是否存在(文件或目录)
print(os.path.exists('/etc/hosts')) # True(在 Linux/macOS 上)
# 专门检查是否为文件
print(os.path.isfile('main.py')) # True
print(os.path.isfile('data')) # False(它是目录)
# 专门检查是否为目录
print(os.path.isdir('data')) # True
print(os.path.isdir('main.py')) # False分解路径
无需手动字符串分割即可从文件路径中提取组件。
import os
filepath = '/home/user/projects/report_final.csv'
# 获取文件名
print(os.path.basename(filepath)) # 'report_final.csv'
# 获取目录
print(os.path.dirname(filepath)) # '/home/user/projects'
# 分割为目录和文件名
directory, filename = os.path.split(filepath)
print(directory) # '/home/user/projects'
print(filename) # 'report_final.csv'
# 分割文件名和扩展名
name, ext = os.path.splitext(filepath)
print(name) # '/home/user/projects/report_final'
print(ext) # '.csv'
# 从相对路径获取绝对路径
print(os.path.abspath('data.csv')) # '/home/user/projects/data.csv'
# 解析用户主目录
print(os.path.expanduser('~/Documents')) # '/home/user/Documents'路径操作快速参考
| 函数 | 用途 | 输出示例 |
|---|---|---|
os.path.join('a', 'b.txt') | 构建路径 | 'a/b.txt' |
os.path.exists(path) | 路径存在? | True / False |
os.path.isfile(path) | 是文件? | True / False |
os.path.isdir(path) | 是目录? | True / False |
os.path.basename(path) | 仅文件名 | 'report.csv' |
os.path.dirname(path) | 仅目录 | '/home/user' |
os.path.splitext(path) | 分割名称+扩展名 | ('report', '.csv') |
os.path.abspath(path) | 绝对路径 | '/full/path/to/file' |
os.path.getsize(path) | 文件大小(字节) | 4096 |
os.path.expanduser('~') | 主目录 | '/home/user' |
环境变量
os 模块提供对系统环境变量的直接访问——对于读取配置、API 密钥和部署设置至关重要。
import os
# 读取环境变量(如果未设置返回 None)
db_host = os.getenv('DATABASE_HOST')
print(db_host)
# 带默认值读取
db_port = os.getenv('DATABASE_PORT', '5432')
print(db_port) # 如果 DATABASE_PORT 未设置则为 '5432'
# 通过 os.environ 字典访问(如果缺失引发 KeyError)
try:
secret = os.environ['API_SECRET']
except KeyError:
print("API_SECRET 未配置")
# 设置环境变量(用于子进程)
os.environ['APP_MODE'] = 'production'
# 列出所有环境变量
for key, value in os.environ.items():
print(f"{key}={value}")os.getenv() 和 os.environ[] 之间的区别很重要:getenv 在变量缺失时返回 None(或默认值),而 os.environ[] 引发 KeyError。对可选配置使用 getenv,对缺失时应该大声报错的必需设置使用 os.environ。
使用 os.walk 遍历目录树
os.walk() 递归遍历目录树,为访问的每个目录生成一个三元组 (dirpath, dirnames, filenames)。
import os
# 遍历项目目录
for dirpath, dirnames, filenames in os.walk('/home/user/project'):
# 跳过隐藏目录
dirnames[:] = [d for d in dirnames if not d.startswith('.')]
print(f"\n目录: {dirpath}")
print(f" 子目录: {dirnames}")
print(f" 文件: {filenames}")dirnames[:] 的原地修改控制 os.walk 进入哪些子目录。这是跳过 .git、__pycache__ 或 node_modules 目录的强大模式。
计算目录总大小
import os
def get_directory_size(path):
total = 0
for dirpath, dirnames, filenames in os.walk(path):
for filename in filenames:
filepath = os.path.join(dirpath, filename)
# 跳过符号链接
if not os.path.islink(filepath):
total += os.path.getsize(filepath)
return total
size_bytes = get_directory_size('/home/user/project')
size_mb = size_bytes / (1024 * 1024)
print(f"总大小: {size_mb:.2f} MB")常见模式
按扩展名查找所有文件
import os
def find_files(directory, extension):
"""递归查找具有给定扩展名的所有文件。"""
matches = []
for dirpath, dirnames, filenames in os.walk(directory):
for filename in filenames:
if filename.endswith(extension):
matches.append(os.path.join(dirpath, filename))
return matches
# 查找所有 Python 文件
python_files = find_files('/home/user/project', '.py')
for f in python_files:
print(f)
# 查找所有 CSV 数据文件
csv_files = find_files('data', '.csv')
print(f"找到 {len(csv_files)} 个 CSV 文件")创建嵌套输出结构
import os
def setup_project_dirs(base_path):
"""创建标准项目目录结构。"""
dirs = [
'data/raw',
'data/processed',
'data/output',
'logs',
'config',
'reports/figures',
]
for d in dirs:
full_path = os.path.join(base_path, d)
os.makedirs(full_path, exist_ok=True)
print(f"已创建: {full_path}")
setup_project_dirs('my_project')使用临时文件的安全文件操作
import os
import tempfile
# 创建自动删除的临时文件
with tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False) as tmp:
tmp.write('col1,col2\n1,2\n3,4\n')
tmp_path = tmp.name
print(f"临时文件: {tmp_path}")
print(f"存在: {os.path.exists(tmp_path)}")
# 清理
os.remove(tmp_path)os.path vs pathlib:你应该用哪个?
Python 3.4 引入了 pathlib 作为 os.path 的面向对象替代方案。两者都可以工作,但有不同的优势。
| 特性 | os.path | pathlib |
|---|---|---|
| 风格 | 函数式(基于字符串) | 面向对象 |
| 可用版本 | Python 2 | Python 3.4+ |
| 路径拼接 | os.path.join('a', 'b') | Path('a') / 'b' |
| 检查存在 | os.path.exists(p) | p.exists() |
| 读取文件 | open(os.path.join(d, f)) | Path(d, f).read_text() |
| Glob 模式 | import glob; glob.glob(...) | Path('.').glob('*.py') |
| 递归 Glob | os.walk() + 过滤 | Path('.').rglob('*.py') |
| 文件扩展名 | os.path.splitext(f)[1] | p.suffix |
| 文件主名 | os.path.splitext(os.path.basename(f))[0] | p.stem |
| 跨平台 | 是 | 是 |
| 第三方兼容性 | 通用 | 大多数库接受 Path 对象 |
何时使用 os.path: 遗留代码库、必须支持 Python 2 的脚本,或使用只接受字符串路径的库时。
何时使用 pathlib: 新项目、想要更简洁的语法时,或链接多个路径操作时。
# os.path 方式
import os
config_path = os.path.join(os.path.expanduser('~'), '.config', 'myapp', 'settings.json')
if os.path.isfile(config_path):
with open(config_path) as f:
data = f.read()
# pathlib 方式
from pathlib import Path
config_path = Path.home() / '.config' / 'myapp' / 'settings.json'
if config_path.is_file():
data = config_path.read_text()两种方式都是有效的。os 模块并未弃用,仍然是进程级操作、环境变量和底层系统调用的标准。pathlib 只是为路径操作提供了更符合人体工学的 API。
跨平台注意事项
os 模块自动适应当前操作系统,但有一些值得了解的细节。
import os
# os.sep 是平台路径分隔符
print(os.sep) # Unix 上 '/',Windows 上 '\\'
# os.linesep 是平台换行符
print(repr(os.linesep)) # Unix 上 '\n',Windows 上 '\r\n'
# os.name 标识操作系统族
print(os.name) # Linux/macOS 上 'posix',Windows 上 'nt'
# os.path.join 自动处理分隔符
path = os.path.join('data', 'output', 'results.csv')
# Unix 上 'data/output/results.csv'
# Windows 上 'data\\output\\results.csv'跨平台脚本的关键规则:
- 始终使用
os.path.join()而不是用/或\\拼接字符串。 - 使用
os.path.expanduser('~')而不是硬编码/home/username。 - 使用
os.linesep或以文本模式打开文件(会处理换行符)而不是硬编码\n。 - 如果你的脚本将被共享,在两个平台上测试路径逻辑。
使用 RunCell 自动化文件系统任务
在 Jupyter 笔记本中处理文件系统操作时——组织数据集、设置项目结构或审计文件树——RunCell (opens in a new tab) 在你的笔记本环境之上添加了一个 AI 代理层。你可以描述你想要的内容("找到这个目录树中所有超过 100MB 的 CSV 文件并按大小列出"),RunCell 会为你生成并运行 os 模块代码,使重复的文件管理任务更快完成。
FAQ
Python 中的 os 模块是什么?
os 模块是 Python 标准库的一部分。它提供了与操作系统交互的函数,包括文件和目录操作、路径处理、环境变量访问和进程管理。使用 import os 导入——无需安装。
os.path.join 和字符串拼接路径有什么区别?
os.path.join() 自动使用当前操作系统的正确路径分隔符(Unix 上为 /,Windows 上为 \)。使用 + 进行字符串拼接需要手动插入分隔符,当代码在不同平台运行时会导致 bug。始终使用 os.path.join()。
如何递归列出目录中的所有文件?
使用 os.walk() 遍历目录树。它为每个目录生成 (dirpath, dirnames, filenames) 元组。结合 os.path.join(dirpath, filename) 构建每个文件的完整路径。
应该使用 os.path 还是 pathlib?
对于新的 Python 3 项目,pathlib 以其面向对象的 API 提供更简洁、更可读的语法。但是,os 和 os.path 并未弃用,对于环境变量(os.environ)、进程操作和遗留代码库仍然是正确的选择。许多项目两者都使用。
如何在 Python 中安全地删除文件或目录?
使用 os.remove(path) 删除文件,os.rmdir(path) 删除空目录。始终先检查 os.path.exists(path) 或将调用包装在 try/except 块中以处理 FileNotFoundError。对于非空目录,使用 shutil.rmtree(path)。
总结
Python 的 os 模块是脚本执行的每个文件系统操作的基础。os.path.join() 跨平台安全地构建路径。os.makedirs() 在单次调用中创建嵌套目录。os.walk() 遍历整个目录树。os.environ 和 os.getenv() 在不硬编码秘密的情况下处理配置。而 os.stat() 为你提供详细的文件元数据。
对于路径密集的代码,考虑将 os 与 pathlib 结合使用以获得更简洁的语法。但 os 模块仍然是不可或缺的——它是 Python 中与操作系统交互的标准方式,每个 Python 开发者都应该了解其核心函数。