下载文件夹里300个文件躺成一片,找一张上周的截图要翻两分钟?
今天写一个“自动收纳师”,写完当天就能用上。
本篇成品:一条命令按类型整理任意文件夹——图片、文档、压缩包各归各位,重名文件绝不覆盖。还带预览模式:先看它打算干什么,再决定让它干。 含注释120行左右。
太长不看版(给想快速上手的你)
| 项目信息 | 一句话说明 |
|---|---|
| 项目名称 | 下载文件夹整理助手(file organizer) |
| 代码行数 | ~120行(含注释) |
| 依赖 | 零依赖,python自带模块 |
| 核心功能 | 按扩展名自动归类 + 重名保护 + 预览模式 |
| 跑起来的命令 | python main.py ~/downloads --dry-run(先预览再执行) |
| 技术点 | pathlib、shutil.move、argparse开关参数 |
| 做完你能得到 | 第一个能真正改变你电脑文件的工具 |
一、成品预览
$ python main.py ~/downloads --dry-run [预览] 报告.pdf → 文档/ [预览] img_2026.jpg → 图片/ [预览] setup.exe → 安装包/ [预览] 神曲.mp3 → 音频/ [预览] 未命名.psd → 其他/ —— 预览 5 个文件,未做任何改动 —— (预览模式:未做任何改动;去掉 --dry-run 正式执行) $ python main.py ~/downloads 已移动:报告.pdf → 文档/ 已移动:img_2026.jpg → 图片/ 已移动:setup.exe → 安装包/ 已移动:神曲.mp3 → 音频/ 已移动:未命名.psd → 其他/ —— 整理 5 个文件 —— $ ls ~/downloads # windows用dir 文档 图片 安装包 音频 其他
和上一篇的待办清单比,这个项目特殊在哪?详情参考:基于python编写一个命令行待办清单的实战教学
它会动你磁盘上的真实文件。 所以本篇除了代码,还有三个安全设计:
| 安全设计 | 作用 |
|---|---|
| 预览模式(dry-run) | 先看它打算干什么,确认无误再执行 |
| 重名不覆盖 | 两个报告.pdf → 第二个自动变报告(1).pdf |
| 只动文件不递归 | 只整理当前层,不动子文件夹里的文件 |
这三个习惯,会让你以后写的每一个“会改东西”的脚本都值得信任。
二、前置知识:pathlib四件套 + 一个搬运工
上一项目见过path,这次用它的四个核心技能:
| 技能 | 写法 | 说明 |
|---|---|---|
| 遍历文件夹 | folder.iterdir() | 逐个给出里面的文件和文件夹 |
| 判断 | item.is_dir() / dest.exists() | 是文件夹吗 / 路径存在吗 |
| 拆文件名 | item.suffix / item.stem | 报告.pdf → 后缀".pdf"、名字"报告" |
| 换个名字 | dest.with_name("新名.pdf") | 同文件夹下改个名字 |
再配一个搬运工:shutil.move(源, 目标) —— 把文件从一个路径移到另一个路径。
注意:item.suffix返回的后缀自带一个点(".pdf"而不是"pdf")。记住这个细节,第九节③有个著名的坑等着它。
三、第0步:setup(3分钟,流程已是老朋友)
1. 新建文件夹organizer,vscode打开
2. python -m venv venv
3. 激活:windows: venv\scripts\activate(cmd) | .\venv\scripts\activate.ps1(powershell)
mac: source venv/bin/activate
4. git init,写好.gitignore
5. 新建空文件:main.py
零依赖项目,venv照建——习惯不因项目而变。
四、第1步:规则表——把"分类逻辑"写成一份数据
先别急着写功能代码。分类的本质是什么?查表:拿到扩展名,查它属于哪一类。那就把表写出来:
"""organizer —— 下载文件夹整理助手"""
import argparse
import shutil
from pathlib import path
# 扩展名 → 文件夹 的归类规则(想加类别,在这里加一行就行)
rules = {
"图片": [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"],
"文档": [".pdf", ".docx", ".doc", ".txt", ".md", ".xlsx", ".pptx"],
"压缩包": [".zip", ".rar", ".7z", ".tar", ".gz"],
"安装包": [".exe", ".msi", ".dmg", ".pkg"],
"视频": [".mp4", ".mkv", ".mov", ".avi"],
"音频": [".mp3", ".wav", ".flac", ".m4a"],
}
def classify(suffix: str) -> str:
"""查规则表:这个扩展名该进哪个文件夹;都不认识就进"其他" """
for folder, exts in rules.items():
if suffix.lower() in exts: # .lower() 让 .jpg 和 .jpg 一视同仁
return folder
return "其他"
体会一下这个设计
| 好在哪里 | 说明 |
|---|---|
| 数据驱动 | 分类规则全在rules里,classify只是查表的人 |
| 易扩展 | 以后想加“电子书”类别,改数据就行,一行逻辑代码都不用动 |
| 容错 | suffix.lower()让.jpg和.jpg一视同仁 |
把变化放进数据,把逻辑写成通用——这是好程序的第一个形状。
五、第2步:重名保护——数据无价(重点!)
如果不做任何保护,两个都叫报告.pdf的文件,后到的会悄悄覆盖先到的——没有任何提示,丢了就是丢了。
def unique_path(dest: path) -> path:
"""目标已存在就起个不重名的新名字:报告.pdf → 报告(1).pdf"""
if not dest.exists():
return dest
stem, ext = dest.stem, dest.suffix
n = 1
while true:
candidate = dest.with_name(f"{stem}({n}){ext}")
if not candidate.exists():
return candidate
n += 1
逻辑简单粗暴:
报告.pdf 被占了 → 试试 报告(1).pdf
报告(1).pdf 也被占了 → 试试 报告(2).pdf
直到没人用为止 ✅
以后你写任何涉及移动、复制、写文件的代码,都先想想这句口诀:
写文件操作前,先问“会不会覆盖”。
六、第3步:主流程——遍历、跳过、预览或搬移
def organize(folder: path, dry_run: bool = false):
"""主流程:遍历 → 分类 → 预览或搬移"""
moved = 0
for item in sorted(folder.iterdir()):
# 跳过文件夹和隐藏文件(.ds_store这类)
if item.is_dir() or item.name.startswith("."):
continue
target_dir = folder / classify(item.suffix)
dest = unique_path(target_dir / item.name)
if dry_run:
print(f"[预览] {item.name} → {target_dir.name}/")
else:
target_dir.mkdir(exist_ok=true) # 文件夹不存在就建
shutil.move(str(item), str(dest))
print(f"已移动:{item.name} → {target_dir.name}/")
moved += 1
action = "预览" if dry_run else "整理"
print(f"—— {action} {moved} 个文件 ——")
if dry_run:
print("(预览模式:未做任何改动;去掉 --dry-run 正式执行)")
三个安全细节都在这一段里
| 安全点 | 代码 | 作用 |
|---|---|---|
| 不搬文件夹 | if item.is_dir(): continue | 包括我们自己生成的归类文件夹,不会搬自己家 |
| 不搬隐藏文件 | if item.name.startswith(".") | 跳过mac的.ds_store等系统文件 |
| 预览/执行分离 | if dry_run: | 同一个逻辑,只差“搬”这一个动作 |
预览模式的精髓:计划与执行分离。 先看计划,再决定是否执行。
七、第4步:argparse接上两个开关
def main():
parser = argparse.argumentparser(description="下载文件夹整理助手")
parser.add_argument("folder", nargs="?", default=".",
help="要整理的文件夹路径,默认当前目录")
parser.add_argument("--dry-run", action="store_true",
help="预览模式:只显示计划,不动任何文件")
args = parser.parse_args()
folder = path(args.folder).expanduser() # ~ 自动展开成用户主目录
if not folder.is_dir():
print(f"文件夹不存在:{folder}")
return
organize(folder, dry_run=args.dry_run)
if __name__ == "__main__":
main()
两个新知识点
| 知识点 | 写法 | 效果 |
|---|---|---|
| 可选参数 | nargs="?" + default="." | 不传路径就整理当前目录 |
| 开关参数 | action="store_true" | 写了--dry-run它就是true,不写就是false |
| 路径展开 | .expanduser() | 把~翻译成你的主目录(/users/你或c:\users\你) |
八、第5步:验收——先沙盒,后实战(重点!)
文件操作脚本有一条铁律:先在沙盒里验证,再上真实数据。 千万别拿下载文件夹当小白鼠。
第一步:造一个沙盒并塞满假文件
vscode终端执行:
# 建沙盒 mkdir sandbox && cd sandbox # 造假文件(windows用 echo hi> 文件名) echo hi > 报告.pdf echo hi > 神曲.mp3 echo hi > setup.exe echo hi > img_001.jpg echo hi > 未知.xyz # 回到项目根目录 cd ..
第二步:沙盒里先预览再实跑
python main.py sandbox --dry-run # 看计划:5个文件各归各类 python main.py sandbox # 正式执行 ls sandbox # 图片/ 音频/ 安装包/ 文档/ 其他/,全齐
第三步:真实战场,先预览
python main.py ~/downloads --dry-run
盯着清单看10秒——有没有不该动的文件?确认无误,去掉--dry-run正式执行。
从此你的下载文件夹,一条命令治好。
九、常见报错:这6个,全是“动文件”脚本的高发区(重点!)
路径带空格被切断:c:\users\my name\downloads只认出了c:\users\my
原因:空格是参数分隔符。
解法:整个路径套引号:
python main.py "c:\users\my name\downloads"
permissionerror: [errno 13],移动到一半中断
原因:文件正被别的程序占用——pdf还开着、视频还在播放器里、下载还没完成。
解法:
- 关掉占用它的程序再跑;
- 本脚本可以放心重跑:已搬好的会跳过(在文件夹里,是目录),没搬的继续搬。
所有文件都进了"其他"
原因:规则表里写的是"pdf",不是".pdf"——suffix自带点(.pdf),查表永远查不中。
解法:用断点法,盯一眼item.suffix的真实长相,和你规则表里的键对一对。
数据对不上,先看数据的真实形状。
手机拍的.jpg全进了“其他”
原因:classify里忘了suffix.lower(),大写后缀查不到表。
解法:代码里已经防了;如果你自查,造一个.jpg假文件跑沙盒试试——这就是沙盒的第二个用处:给边界情况做回归测试。
“其他”文件夹里混着.ds_store(mac)
原因:macos会往文件夹里塞隐藏配置文件,它们也是“文件”,会被遍历到。
解法:startswith(".")已经跳过它们。windows的隐藏文件(desktop.ini)也可以按这个逻辑处理。
⑥
原因:shutil.move(item, target_dir / item.name)遇到同名文件时,行为会因操作系统而异——覆盖或报错,都不受你控制。
解法:永远走unique_path。 再念一遍口诀:
写文件操作前,先问“会不会覆盖”。
十、课后练习
| # | 练习 | 难度 | 提示 |
|---|---|---|---|
| 1 | 加类别:给rules加“电子书”(.epub/.mobi/.azw3)和“代码”(.py/.ipynb/.html) | ⭐ | 改数据不改逻辑,体会有多爽 |
| 2 | 安全升级:把默认行为改成“只预览”,加一个--yes参数才真正执行 | ⭐⭐ | action="store_true",逻辑反过来 |
| 3 | 自动化测试雏形:用循环造20个假文件,跑整理,断言各类别文件数正确 | ⭐⭐⭐ | path(f"报告{i}.pdf").touch() |
| 4(选做) | 定时自动整理:windows用“任务计划程序”,mac用launchd,每天21:00自动执行 | ⭐⭐⭐⭐ | 务必先跑过--dry-run确认无误再上定时 |
以上就是基于python编写一个自动整理文件夹工具的详细内容,更多关于python文件夹整理的资料请关注代码网其它相关文章!
发表评论