文章以 os、os.path、glob、shutil、pathlib 库为主线,对每个函数/方法的参数、返回值、注意事项、典型用法进行详细拆解,力求让你看完之后全面掌握文件处理操作。
一、os 模块 – 操作系统接口
1.1 目录操作
os.getcwd()
os.getcwd() -> str
- 注意:该路径是 Python 进程启动时的工作目录,不一定是脚本所在目录。
import osprint(os.getcwd())
os.chdir(path)
os.chdir(path: Union[str, bytes, PathLike]) -> None
- 作用:将当前工作目录更改为
path,类似于 shell 的 cd 命令。 - 参数:
path – 目标目录路径(字符串或 bytes 或 path-like 对象)。 - 异常:
FileNotFoundError(路径不存在)、NotADirectoryError(路径不是目录)、PermissionError(无权限)。
os.chdir("/tmp")
print(os.getcwd())
os.listdir(path='.')
os.listdir(path: Union[str, bytes, PathLike] = '.') -> List[str]
- 作用:返回指定目录下所有条目名称(不含完整路径)的列表,不包含
. (当前目录)和 ..(上一级目录)。 - 注意:返回的列表顺序不确定(通常按文件系统内部顺序)。
entries = os.listdir("/home/user")
os.mkdir(path, mode=0o777, *, dir_fd=None)
os.mkdir(path: Union[str, bytes, PathLike], mode: int = 0o777, *, dir_fd: Optional[int] = None) -> None
- 作用:创建单级目录。父目录必须已经存在,否则抛出
FileNotFoundError。 mode – 权限模式(八进制),仅在 Unix 有效,Windows 忽略。
- 异常:
FileExistsError – 目录已存在;FileNotFoundError – 父目录不存在。
os.mkdir("new_folder") # 创建 new_folderos.mkdir("/root/secret", mode=0o700) # 仅所有者可读写执行
os.makedirs(path, mode=0o777, exist_ok=False)
os.makedirs(name: Union[str, bytes, PathLike], mode: int = 0o777, exist_ok: bool = False) -> None
exist_ok=False ,如果为 True,当目标目录已存在时不抛出异常;如果为 False 且目录存在则抛出 FileExistsError。
# 创建 a/b/c 三级目录,父目录不存在也会自动创建os.makedirs("a/b/c", exist_ok=True)
os.rmdir(path, *, dir_fd=None)
os.rmdir(path: Union[str, bytes, PathLike], *, dir_fd: Optional[int] = None) -> None
- 作用:删除空目录。如果目录非空,抛出
OSError(错误码 39 "Directory not empty")。 - 注意:要删除非空目录,请使用
shutil.rmtree()。
os.rmdir("empty_folder") # 成功os.rmdir("non_empty") # OSError
os.removedirs(name)
os.removedirs(name: Union[str, bytes, PathLike]) -> None
- 作用:递归删除目录,从给定的路径开始自底向上删除空目录,遇到非空目录即停止。
# 假设目录结构:a/b/c,且 c 为空os.removedirs("a/b/c")
os.walk(top, topdown=True, onerror=None, followlinks=False)
os.walk(top: Union[str, bytes, PathLike], topdown: bool = True, onerror: Optional[Callable] = None, followlinks: bool = False) -> Iterator[Tuple[str, List[str], List[str]]]
- 作用:遍历目录树,生成三元组
(dirpath, dirnames, filenames)。 topdown=True – 若为 True,先遍历顶层目录再进入子目录;若为 False,先进入子目录再返回顶层(后序遍历)。onerror – 当访问目录出错时的回调函数,默认抛出异常。followlinks=False – 若为 True,会跟随符号链接(可能导致循环链接无限递归)。
- 返回值:生成器,每次迭代返回
(dirpath, dirnames, filenames)。 dirpath – 当前遍历的目录路径(字符串)。dirnames – 该目录下的子目录名称列表(可修改,用于控制遍历)。filenames – 该目录下的非目录文件名称列表。
for root, dirs, files in os.walk("/home/user"):for file in files:if file.endswith(".py"): print(os.path.join(root, file))
1.2 文件操作
os.remove(path, *, dir_fd=None)
os.unlink(path, *, dir_fd=None)
os.remove(path: Union[str, bytes, PathLike], *, dir_fd: Optional[int] = None) -> None
- 作用:删除文件(不能删除目录)。两个函数完全等价。
- 异常:
IsADirectoryError – 试图删除目录;FileNotFoundError – 文件不存在。
os.remove("temp.txt")
os.rename(src, dst, *, src_dir_fd=None, dst_dir_fd=None)
os.rename(src: Union[str, bytes, PathLike], dst: Union[str, bytes, PathLike], *, src_dir_fd: Optional[int] = None, dst_dir_fd: Optional[int] = None) -> None
- 注意:如果
dst 已存在,Unix 下会覆盖;Windows 下抛出 FileExistsError。 - 跨设备移动会失败,此时应使用
shutil.move()。
os.rename("old.txt", "new.txt") # 重命名os.rename("file.txt", "subdir/file.txt") # 移动(同一设备)
os.stat(path, *, dir_fd=None, follow_symlinks=True)
os.stat(path: Union[str, bytes, PathLike], *, dir_fd: Optional[int] = None, follow_symlinks: bool = True) -> os.stat_result
- 作用:获取文件/目录的元数据,返回
stat_result 对象(类元组)。 - 常用属性:
| | |
|---|
st_size | | |
st_mtime | | |
st_ctime | 元数据变更时间(Unix)或创建时间(Windows) | |
st_atime | | |
st_mode | | |
st_uid | | |
info = os.stat("data.bin")print(f"大小: {info.st_size} 字节, 修改时间: {info.st_mtime}")
1.3 os.path 子模块 – 路径字符串处理
os.path.exists(path)
os.path.exists(path: Union[str, bytes, PathLike]) -> bool
os.path.isfile(path) / os.path.isdir(path)
os.path.isfile(path: Union[str, bytes, PathLike]) -> boolos.path.isdir(path: Union[str, bytes, PathLike]) -> bool
- 作用:判断是否为普通文件/目录。如果路径是符号链接,会解析链接后判断。
os.path.join(a, *paths)
os.path.join(a: Union[str, bytes, PathLike], *paths: Union[str, bytes, PathLike]) -> str
- 作用:智能拼接路径,使用当前操作系统的路径分隔符(Windows 用
\,其他用 /)。 - 如果某个参数是绝对路径,则它之前的所有参数被丢弃。
os.path.join("home", "user", "data.txt") # 'home/user/data.txt' (Linux)os.path.join("C:", "Users", "file.txt") # 'C:/Users/file.txt' (Windows)
os.path.dirname(path) / os.path.basename(path)
os.path.dirname(path: Union[str, bytes, PathLike]) -> stros.path.basename(path: Union[str, bytes, PathLike]) -> str
p = "/home/user/file.txt"os.path.dirname(p) # '/home/user'os.path.basename(p) # 'file.txt'
os.path.splitext(path)
os.path.splitext(path: Union[str, bytes, PathLike]) -> Tuple[str, str]
- 作用:分离文件名和扩展名(最后一个点之后的部分)。
- 注意:以
. 开头的文件(如 .bashrc)被视为无扩展名。
os.path.splitext("archive.tar.gz") # ('archive.tar', '.gz')os.path.splitext(".hidden") # ('.hidden', '')
os.path.abspath(path)
os.path.abspath(path: Union[str, bytes, PathLike]) -> str
- 作用:返回规范化后的绝对路径(解析
..、.,不解析符号链接)。
os.path.abspath("docs/../readme.md") # '/current/working/dir/readme.md'
os.path.getsize(path)
os.path.getsize(path: Union[str, bytes, PathLike]) -> int
- 作用:返回文件大小(字节)。对目录的行为依赖于系统(通常返回 0 或 4096,不建议用于目录)。
1.4 环境变量与进程
os.environ
os.environ : os._Environ
- 本质:一个类似字典的对象,包含当前进程的环境变量。
# 获取(不存在返回 None,推荐 get)path = os.environ.get("PATH", "/usr/bin")# 设置(仅影响当前进程及子进程)os.environ["MY_VAR"] = "value"# 删除del os.environ["TEMP"]
os.system(command)
os.system(command: str) -> int
- 作用:在子 shell 中执行命令,返回退出状态码(0 表示成功)。
- 警告:不安全(易受 shell 注入攻击),不推荐使用。建议使用
subprocess.run()。
os.system("ls -l") # 显示文件列表,返回 0
二、glob 模块 – Unix 风格路径模式匹配
glob.glob(pathname, *, recursive=False)
glob.glob(pathname: str, *, recursive: bool = False) -> List[str]
- 作用:返回匹配
pathname 的所有路径列表,结果未排序(依赖系统)。 recursive=False – 若为 True,则 ** 匹配任意文件及零个或多个目录/子目录。
- 通配符详解:
| | | |
|---|
* | | *.txt | a.txt |
** | 递归匹配所有目录(需 recursive=True) | **/*.py | a.py, sub/b.py, sub/sub2/c.py |
? | | data?.csv | data1.csv |
[seq] | | [abc]*.txt | a.txt |
[!seq] | | [!0-9]*.txt | a.txt |
- 注意:
glob.glob 默认不匹配以点开头的文件(如 .bashrc),除非模式显式以点开头。
glob.iglob(pathname, *, recursive=False)
glob.iglob(pathname: str, *, recursive: bool = False) -> Iterator[str]
- 作用:与
glob 相同,但返回迭代器而非列表,适合处理海量文件(内存友好)。
for file in glob.iglob("logs/*.log"): process(file) # 逐个处理,不一次性加载所有路径
glob.escape(pathname)
glob.escape(pathname: str) -> str
- 作用:转义模式中的特殊字符(
*、?、[、]),使其被当作普通字符匹配。
pattern = glob.escape("a[b].txt") # 'a\\[b\\].txt'glob.glob(pattern) # 只匹配字面量 "a[b].txt"
glob.translate(pathname)
glob.translate(pathname: str) -> str
- 作用:将 glob 模式转换为正则表达式(可用于
re 模块进一步匹配)。
regex = glob.translate("*.txt")print(regex) # '.*\\.txt\\Z(?ms)'
三、shutil 模块 – 高级文件操作
3.1 复制操作
shutil.copy(src, dst, *, follow_symlinks=True)
shutil.copy(src: Union[str, bytes, PathLike], dst: Union[str, bytes, PathLike], *, follow_symlinks: bool = True) -> str
- 作用:复制文件
src 到 dst,保留文件权限模式(但不保留元数据如修改时间)。 dst – 可以是文件名(重命名)或目录名(保留原文件名复制到该目录)。follow_symlinks=False – 复制符号链接本身而不是指向的文件。
shutil.copy("source.txt", "dest.txt") # 复制并重命名shutil.copy("source.txt", "backup_dir/") # 复制到目录,保留原文件名
shutil.copy2(src, dst, *, follow_symlinks=True)
shutil.copy2(src, dst, *, follow_symlinks=True) -> str
- 作用:与
copy 类似,但尽量保留元数据(修改时间、访问时间等)。这是 copy + copystat 的组合。
shutil.copyfile(src, dst, *, follow_symlinks=True)
shutil.copyfile(src: Union[str, bytes, PathLike], dst: Union[str, bytes, PathLike], *, follow_symlinks: bool = True) -> str
- 作用:仅复制文件内容(不含权限、元数据)。如果
dst 已存在,会被覆盖。 - 要求:
src 和 dst 必须是文件(不能是目录)。
shutil.copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False, dirs_exist_ok=False)
shutil.copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False, dirs_exist_ok=False) -> str
symlinks=False – 若为 True,复制符号链接本身;若为 False,跟随链接复制其指向的文件。ignore – 可调用对象,用于排除文件/目录,如 ignore=shutil.ignore_patterns("*.tmp", "*.log")。copy_function – 默认 copy2,可替换为 copy 以加快速度。dirs_exist_ok=False – 若为 True,允许目标目录存在(Python 3.8+)。
shutil.copytree("source_dir", "dest_dir", ignore=shutil.ignore_patterns("__pycache__", "*.pyc"))
3.2 移动、删除与磁盘
shutil.move(src, dst, copy_function=copy2)
shutil.move(src: Union[str, bytes, PathLike], dst: Union[str, bytes, PathLike], copy_function: Callable = copy2) -> str
- 先尝试
os.rename()(同一文件系统内是原子的)。 - 如果失败(跨设备),则用
copy_function 复制内容,然后删除源。
shutil.rmtree(path, ignore_errors=False, onerror=None, *, onexc=None)
shutil.rmtree(path: Union[str, bytes, PathLike], ignore_errors: bool = False, onerror: Optional[Callable] = None, *, onexc: Optional[Callable] = None) -> None
- 作用:递归删除整个目录树(包括所有子目录和文件),不可逆。
ignore_errors=True – 忽略删除过程中的错误。onexc – 错误处理回调函数(Python 3.12+),接收 (func, path, excinfo)。
shutil.rmtree("temp_dir") # 删除该目录及其所有内容
shutil.disk_usage(path)
shutil.disk_usage(path: Union[str, bytes, PathLike]) -> shutil._ntuple_diskusage
- 作用:返回磁盘使用情况,命名元组包含
total, used, free(字节数)。
usage = shutil.disk_usage("/")print(f"总空间: {usage.total // (1024**3)} GB")
3.3 归档与解压
shutil.make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, dry_run=0, owner=None, group=None, logger=None)
shutil.make_archive(base_name: str, format: str, root_dir: Optional[str] = None, base_dir: Optional[str] = None, ...) -> str
base_name – 输出文件的前缀(不含扩展名)。format – 支持 'zip', 'tar', 'gztar', 'bztar', 'xztar'。root_dir – 归档的根目录(所有路径相对于此)。base_dir – 要归档的目录(默认为当前目录)。
shutil.make_archive("backup", "zip", root_dir="/home/user/data")
shutil.unpack_archive(filename, extract_dir=None, format=None)
shutil.unpack_archive(filename: Union[str, bytes, PathLike], extract_dir: Optional[Union[str, bytes, PathLike]] = None, format: Optional[str] = None) -> None
extract_dir – 解压目标目录,默认当前目录。format – 自动检测(无需指定),可强制指定。
shutil.unpack_archive("backup.zip", "output/")
四、pathlib 模块 – 面向对象的路径
4.1 创建 Path 对象
from pathlib import Path# 当前目录p = Path()# 绝对路径p = Path("/home/user/file.txt")# 相对路径p = Path("data/input.csv")# 组合(推荐)p = Path.home() / "documents" / "notes.txt"
4.2 路径组件访问
| | |
|---|
p.name | 'file.txt' | |
p.stem | 'file' | |
p.suffix | '.txt' | |
p.suffixes | ['.tar', '.gz'] | |
p.parent | Path('/home/user') | |
p.parents | [Path('/home/user'), Path('/home'), Path('/')] | |
p.anchor | '/' (Linux) 或 'C:\\' (Windows) | |
p.drive | 'C:' | |
p.root | '\\' | |
4.3 目录操作
Path.mkdir(mode=0o777, parents=False, exist_ok=False)
defmkdir(self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False) -> None
parents=True – 自动创建不存在的父目录(类似 os.makedirs)。exist_ok=True – 目录已存在时不报错。
Path("a/b/c").mkdir(parents=True, exist_ok=True)
Path.rmdir()
defrmdir(self) -> None
Path.iterdir()
defiterdir(self) -> Iterator[Path]
- 作用:生成当前目录下的所有条目(文件和子目录)的
Path 对象迭代器。
for child in Path(".").iterdir():if child.is_file(): print(f"文件: {child.name}")
Path.walk(top_down=True, on_error=None, follow_symlinks=False)
defwalk(self, top_down: bool = True, on_error: Optional[Callable] = None, follow_symlinks: bool = False) -> Iterator[Tuple[Path, List[str], List[str]]]
- 作用:Python 3.12+ 引入,遍历目录树,生成
(root, dirs, files) 三元组(dirs 和 files 是字符串列表)。
for root, dirs, files in Path("project").walk():for file in files: print(root / file)
4.4 模式匹配
Path.glob(pattern)
defglob(self, pattern: str) -> Iterator[Path]
- 作用:非递归匹配当前目录下的文件/目录(
** 无效),返回生成器。
for py in Path("src").glob("*.py"): print(py)
Path.rglob(pattern)
defrglob(self, pattern: str) -> Iterator[Path]
- 作用:递归匹配,相当于
glob("**/" + pattern)。
# 查找所有子目录中的 .txt 文件txt_files = list(Path(".").rglob("*.txt"))
4.5 文件操作
Path.read_text(encoding=None, errors=None)
defread_text(self, encoding: Optional[str] = None, errors: Optional[str] = None) -> str
- 注意:文件不存在时抛出
FileNotFoundError。
content = Path("config.json").read_text(encoding="utf-8")
Path.write_text(data, encoding=None, errors=None)
defwrite_text(self, data: str, encoding: Optional[str] = None, errors: Optional[str] = None) -> int
- 作用:将字符串写入文件(覆盖模式)。返回写入的字符数。
Path("output.txt").write_text("Hello, World!", encoding="utf-8")
Path.read_bytes() / Path.write_bytes(data)
defread_bytes(self) -> bytesdef write_bytes(self, data: bytes) -> int
Path.open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)
defopen(self, mode: str = 'r', buffering: int = -1, encoding: Optional[str] = None, errors: Optional[str] = None, newline: Optional[str] = None) -> IO
- 作用:打开文件,返回文件对象。与内置
open() 参数相同,但更简洁。
with Path("data.bin").open("rb") as f: data = f.read()
Path.unlink(missing_ok=False)
defunlink(self, missing_ok: bool = False) -> None
- 参数:
missing_ok=True – 文件不存在时不抛出 FileNotFoundError。
Path("temp.txt").unlink(missing_ok=True)
4.6 属性与判断
| |
|---|
p.exists() | |
p.is_file() | |
p.is_dir() | |
p.is_symlink() | |
p.is_absolute() | |
p.stat() | 返回 os.stat_result,用法同 os.stat |
p.resolve() | |
p.absolute() | |
4.7 路径拼接与转换
# 使用 / 运算符home = Path.home()config = home / ".config" / "app" / "settings.ini"# 转换为字符串str_path = str(config) # '/home/user/.config/app/settings.ini'# 转换为 os 风格路径(用于旧 API)os_path = config.as_posix() # 总是使用 '/' 分隔符
五、总结对比表(按函数功能索引)
| | | | |
|---|
| os.getcwd() | Path.cwd() | | |
| os.chdir() | | | |
| os.listdir() | Path.iterdir() | | |
| os.mkdir() | Path.mkdir() | | |
| os.makedirs() | Path.mkdir(parents=True) | | |
| os.rmdir() | Path.rmdir() | | |
| | | shutil.rmtree() | |
| os.remove() | Path.unlink() | | |
| os.rename() | Path.rename() | shutil.move() | |
| | | shutil.copy2() | |
| | | shutil.copytree() | |
| os.path.join() | / | | |
| os.path.exists() | Path.exists() | | |
| os.path.getsize() | Path.stat().st_size | | |
| os.walk() | Path.walk() | | |
| glob.glob() | Path.glob() | | glob.glob() |
| open() | Path.read_text() | | |
| open() | Path.write_text() | | |
| | | shutil.make_archive() | |
| | | shutil.unpack_archive() | |
| os.environ | | | |
| os.system() | | | |
六、常见陷阱与最佳实践
路径拼接不要用字符串加法❌ path = root + "/" + filename(Windows 下错误)✅ os.path.join(root, filename) 或 Path(root) / filename
删除非空目录不要用 os.rmdir❌ os.rmdir("non_empty") → OSError✅ shutil.rmtree("non_empty")
跨设备移动文件不要用 os.rename❌ 跨分区移动会失败✅ shutil.move(src, dst)
遍历目录时修改正在遍历的列表os.walk 中修改 dirnames 可以控制子目录遍历(修剪),但不要直接修改 filenames 列表来删除文件(会导致跳过条目)。删除文件应在遍历到该文件时执行。
glob 不会自动隐藏文件模式 *.txt 不会匹配 .hidden.txt,需显式写 .*.txt 或 .??*。
pathlib 的 / 运算符性能虽然优雅,但频繁大量拼接时,os.path.join 稍快(微秒级差异)。日常使用无感知。
注意 shutil.copy 与 shutil.copy2 的区别备份重要文件时使用 copy2 保留元数据;临时复制用 copy 更快。
安全写入文件(防止写一半崩溃)推荐使用临时文件 + 原子替换:
from pathlib import Pathimport tempfiledefatomic_write(path: Path, content: str):with tempfile.NamedTemporaryFile(dir=path.parent, delete=False) as tf: tf.write(content.encode()) tmp_path = Path(tf.name) tmp_path.replace(path) # 原子操作(Unix)