shutil.make_archive默认将root_dir作为归档根目录,导致解压后多一层文件夹;正确做法是设root_dir为其父目录、base_dir为相对子目录,如root_dir='.'、base_dir='my_project'。

直接用 shutil.make_archive 压缩整个文件夹是可行的,但默认行为会把目标文件夹**作为根目录打包进去**,导致解压后多一层冗余目录——这是绝大多数人踩坑的起点。
为什么压缩后解压多了一层文件夹?
因为 shutil.make_archive 的 root_dir 参数默认就是你要压缩的文件夹路径,它会把该路径当作归档的“根”,所有内容都相对这个根来组织。比如你传入 root_dir='my_project',那归档里顶层就是 my_project/xxx。
常见错误现象:
– 执行 shutil.make_archive('dist', 'zip', 'my_project')
– 解压得到 dist.zip → my_project/... ,而不是想要的 dist.zip → *.py, README.md 等平铺文件
- 根本原因不是函数用错了,而是没理解
root_dir和base_dir的分工 -
root_dir是归档时的“工作根目录”(类似 cd 进去的位置) -
base_dir才是你真正想打包的子目录(相对于root_dir)
正确写法:用 base_dir 控制打包范围
要把 my_project/ 里的内容平铺打进 zip,就得让 root_dir 设为 my_project 的父目录,再用 base_dir='my_project' 指定要打包的子目录。
立即学习“Python免费学习笔记(深入)”;
import shutil
shutil.make_archive(
base_name='dist',
format='zip',
root_dir='.',
base_dir='my_project'
)
这样实际效果等价于:
– 先 cd .
– 再 zip -r dist.zip my_project/
– 解压后直接看到 my_project/ 下的所有文件和子目录,没有额外套壳
- 如果想排除
.git或__pycache__,shutil.make_archive本身不支持过滤,得先用shutil.copytree+ignore构建临时干净目录,再归档 -
format可选值:'zip', 'tar', 'gztar', 'bztar', 'xztar';注意 'gztar' 生成的是.tar.gz,不是纯.gz - 输出路径由
base_name决定:base_name='dist'→dist.zip;若含路径如'./output/dist',会自动创建中间目录
遇到 PermissionError 或空归档怎么办?
常见错误信息:PermissionError: [Errno 13] Permission denied: 'my_project/.git/config',或生成的 zip 打开为空。
- 多数是因为
root_dir权限不足,或路径拼写错误(比如base_dir不存在) - 检查
os.path.exists(root_dir)和os.path.isdir(os.path.join(root_dir, base_dir)) - Windows 下路径分隔符不用手动处理,
shutil内部已适配;但避免硬编码'\'或'//' - 如果
base_dir是绝对路径,root_dir必须设为'/'(Linux/macOS)或驱动器根(如'C:\'),否则行为未定义
最易被忽略的一点:shutil.make_archive 不会自动创建输出目录。如果 base_name 包含子目录(如 'build/dist'),必须确保 build/ 已存在,否则抛 FileNotFoundError。


















