Skip to content

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.pathpathlib
风格函数式(基于字符串)面向对象
可用版本Python 2Python 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')
递归 Globos.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 提供更简洁、更可读的语法。但是,osos.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.environos.getenv() 在不硬编码秘密的情况下处理配置。而 os.stat() 为你提供详细的文件元数据。

对于路径密集的代码,考虑将 ospathlib 结合使用以获得更简洁的语法。但 os 模块仍然是不可或缺的——它是 Python 中与操作系统交互的标准方式,每个 Python 开发者都应该了解其核心函数。

📚