Python shutil模块使用指南
目录
1. 模块定位
shutil(shell utilities)是Python标准库中的高级文件操作模块:
- 提供跨平台的文件/目录管理操作
- 补充os模块的底层文件操作
- 支持归档格式处理(zip/tar等)
- 处理文件权限和元数据
典型应用场景:
import shutil
from pathlib import Path
# 项目部署操作示例
def deploy_project(source_dir: Path, dest_dir: Path):
if dest_dir.exists():
shutil.rmtree(dest_dir)
shutil.copytree(source_dir, dest_dir)
shutil.make_archive(str(dest_dir), 'zip', dest_dir)
2. 核心功能分类
功能矩阵表
类别 | 主要函数 |
---|---|
文件操作 | copyfile(), copy(), copy2(), move(), rmtree() |
目录操作 | copytree(), disk_usage(), chown() |
归档压缩 | make_archive(), unpack_archive(), register_archive_format() |
权限管理 | copymode(), copystat(), ignore_patterns() |
系统命令 | which(), get_terminal_size() |
3. 文件操作详解
基础复制操作
# 元数据不复制(仅内容)
shutil.copyfile('src.txt', 'dst.txt')
# 复制文件+权限
shutil.copy('src.txt', 'dir/')
# 复制文件+所有元数据(包括修改时间)
shutil.copy2('src.txt', 'dir/backup.txt')
高级复制控制
# 自定义复制函数
def _copy_with_progress(src, dst, *, follow_symlinks=True):
print(f"Copying {src} => {dst}")
return shutil.copy2(src, dst, follow_symlinks=follow_symlinks)
shutil.copyfileobj(open('a.txt', 'r'), open('b.txt', 'w'),
length=16*1024, # 缓冲区大小
callback=lambda pos, total: print(f"{pos/total:.1%}"))
移动与删除
# 安全移动文件(跨设备自动复制+删除)
shutil.move('src.txt', '/mnt/backup/')
# 递归删除目录(危险操作!)
shutil.rmtree('temp_dir',
ignore_errors=False, # 是否忽略错误
onerror=lambda func, path, exc_info: print(f"Error in {func}({path})"))
4. 目录操作大全
目录复制
# 基本目录复制
shutil.copytree('src_dir', 'dst_dir',
symlinks=False, # 是否复制符号链接
ignore=shutil.ignore_patterns('*.tmp', '__pycache__'),
copy_function=shutil.copy2,
dirs_exist_ok=True) # Python 3.8+ 允许目标存在
# 自定义过滤逻辑
def ignore_logs(path, names):
return {'access.log', 'error.log'} if Path(path).name == 'logs' else set()
shutil.copytree('app', 'backup', ignore=ignore_logs)
磁盘管理
# 查询磁盘使用情况
usage = shutil.disk_usage('/')
print(f"Total: {usage.total / 1e9:.1f}GB, Used: {usage.used / 1e9:.1f}GB")
# 修改目录所有者(需要root权限)
shutil.chown('data/', user='www-data', group='www-data')
5. 归档压缩处理
创建归档包
# 创建ZIP压缩包
shutil.make_archive('backup_2023', 'zip', 'src_dir')
# 带自定义根目录的tar.gz
shutil.make_archive('package', 'gztar',
root_dir='dist',
base_dir='lib', # 只打包dist/lib目录
verbose=True)
解压操作
# 自动识别格式解压
shutil.unpack_archive('package.tar.gz', extract_dir='dist')
# 处理特殊格式
shutil.register_archive_format('myformat',
lambda name, base_dir, **kw: custom_pack(),
description='My custom format')
6. 高级操作技巧
元数据管理
# 仅复制权限模式
shutil.copymode('source.txt', 'dest.txt')
# 复制所有stat信息
shutil.copystat('source_dir', 'dest_dir')
# 保留原始文件时间戳
def copy_preserve_all(src, dst):
shutil.copy2(src, dst)
st = os.stat(src)
os.chown(dst, st.st_uid, st.st_gid) # 需要权限
进度监控
class ProgressTracker:
def __init__(self, total):
self.total = total
self.processed = 0
def __call__(self, path, names):
self.processed += len(names)
print(f"{self.processed/self.total:.1%} completed")
return set()
# 在copytree中使用
shutil.copytree('big_dir', 'backup',
ignore=ProgressTracker(len(list(Path('big_dir').rglob('*')))))
7. 异常处理机制
错误处理模式
try:
shutil.rmtree('important_dir')
except FileNotFoundError:
print("Directory already gone")
except PermissionError as e:
print(f"Permission denied: {e.filename}")
自定义错误回调
def handle_rmtree_error(func, path, exc_info):
import stat
os.chmod(path, stat.S_IWRITE)
func(path)
shutil.rmtree('locked_dir', onerror=handle_rmtree_error)
8. 性能优化策略
-
大文件复制:
# 使用更大的缓冲区 with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst: shutil.copyfileobj(fsrc, fdst, length=1024*1024) # 1MB buffer
-
并行处理:
from concurrent.futures import ThreadPoolExecutor def copy_item(src_path, dst_path): if src_path.is_dir(): shutil.copytree(src_path, dst_path) else: shutil.copy2(src_path, dst_path) with ThreadPoolExecutor() as executor: futures = [] for item in Path('src_dir').iterdir(): futures.append(executor.submit( copy_item, item, Path('dst_dir')/item.name )) for future in futures: future.result()
9. 注意事项
-
路径处理规范:
# 使用Path对象作为参数(Python 3.6+) shutil.copy(Path('src.txt'), Path('dest.txt'))
-
安全删除策略:
def safe_remove(path: Path): if path.is_dir(): shutil.rmtree(path, onerror=handle_errors) else: path.unlink(missing_ok=True)
-
跨平台注意事项:
# Windows长路径处理 if sys.platform == 'win32': path = '\\\\?\\' + str(Path(long_path).resolve())
10. 危险操作警示
-
不可逆删除:
# 永远不要直接执行这种代码! shutil.rmtree('/', ignore_errors=True) # 毁灭性操作!
-
符号链接陷阱:
# 默认会跟随符号链接 shutil.copytree('src', 'dst', symlinks=False) # 复制链接文件本身
-
权限覆盖风险:
# 可能降低文件安全性 shutil.chown('config.ini', user='nobody') # 需谨慎操作
-
静默覆盖问题:
# move操作可能覆盖现有文件 if dst_path.exists(): raise RuntimeError("Target already exists") shutil.move(src, dst)