操作文件和目录

跨平台写 Python 脚本时,最常踩的坑就是硬编码路径分隔符适配系统命令,或者不知道当前代码跑在哪——这时候 os 家族(含 os.path)、shutilpathlib(Python 3.4+)就是你的救星!

这篇文章带你梳理 Python 中文件/目录操作的核心工具链,附安全提示和实用案例。


操作系统类型检测

先搞清楚脚本的运行环境,能帮你做针对性的适配(比如路径处理、终端调用)。

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.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

虽然现代 Python 推荐用 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

目录操作

不管用 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 自动创建父目录)
(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. 移动时如果目标目录不存在,也可以配合 mkdir
# (new_p.parent).mkdir(parents=True, exist_ok=True)
# old_p.rename(new_p)

# 3. 删除文件
old_p.unlink(missing_ok=True)  # Python 3.8+ 加 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')

实用小技巧

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(面向对象、链式调用、/ 操作符),老项目兼容用 os.path
  • 基础目录/文件操作:用 ospathlib 对应方法
  • 复杂操作:用 shutil(复制、递归删除等)
  • 安全提示shutil.rmtree 要加判断;生产环境不要打印完整 os.environ;操作前尽量检查文件/目录是否存在