在日常开发中,下载文件夹往往堆满各种格式的文件。本文实现一个约120行的Python脚本,通过pathlib遍历目录、shutil.move搬移文件、argparse解析命令行参数,将图片、文档、压缩包等按扩展名自动归类。脚本支持预览模式(--dry-run)和重名保护,避免误移和覆盖。
一、设计思路
这个工具的核心是“查表分类”:拿到文件后缀,去规则表里查它属于哪一类,然后移动到对应子文件夹。分类逻辑全部放在RULES字典中,classify函数只负责查表。
要增加新类别时,只需在RULES里加一行,不需要改动其他代码。
二、需要的前置模块
全部依赖Python标准库,无需安装第三方包。
主要使用以下pathlib技能:
- folder.iterdir():逐个返回文件夹内的文件和子目录
- item.is_dir():判断是否为目录
- item.suffix / item.stem:获取文件后缀(带点)和不带后缀的文件名
- dest.with_name("新名.pdf"):同一目录下构造新文件名
以及shutil.move(source, target),完成跨目录移动。
注意:suffix返回的值自带点号,例如"报告.pdf"的suffix是".pdf"而不是"pdf",后续写规则表时需要对应。
三、代码实现
- 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:
- return folder
- return "其他"
- def unique_path(dest: Path) -> Path:
- """重名保护:已存在时生成 名字(1).ext 等新路径"""
- 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
- def organize(folder: Path, dry_run: bool = False):
- """遍历当前层文件,分类并移动;dry_run为True时不执行移动"""
- moved = 0
- for item in sorted(folder.iterdir()):
- 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 正式执行)")
- 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()
复制代码
四、关键机制说明
1. 分类规则采用数据驱动。
分类逻辑集中在RULES字典中,classify函数只是做线性查表。
2. 重名保护使用unique_path函数。
当目标路径已存在时,依次尝试“文件名(1).ext”、“文件名(2).ext”直到找到可用名称。
这能防止两个同名文件移动时互相覆盖。
3. 预览与执行分离。
--dry-run参数让程序只打印计划,不执行shutil.move。
确认无误后去掉参数再运行,可以避免误操作。
4. 只整理当前层文件。
遇到子目录直接跳过,不递归处理,也不移动已经生成的分类目录。
隐藏文件(如.DS_Store、.git)因以点开头被跳过。
五、命令行参数设计
- folder:位置参数,可以传入目标路径,不传时默认整理当前目录。
- --dry-run:开关参数,使用action="store_true",不需要额外值;写了就是True,不写就是False。
- fold.expanduser():将"~"自动展开为用户主目录,兼容Mac和Windows的路径写法。
调用方式:
先预览:- python main.py ~/Downloads --dry-run
复制代码
确认无误后实际执行:- python main.py ~/Downloads
复制代码
路径中含空格时,必须用引号包围:
- python main.py "C:\Users\my name\Downloads"
复制代码
六、沙盒验收流程
直接对真实下载目录运行文件操作脚本有风险。建议先建立沙盒验证。
在项目目录下创建sandbox文件夹并填充假文件:
- mkdir sandbox && cd sandbox
- echo hi > 报告.pdf
- echo hi > 神曲.mp3
- echo hi > setup.exe
- echo hi > IMG_001.jpg
- echo hi > 未知.xyz
- cd ..
复制代码
Windows下可用 echo hi> 文件名 创建。
然后分别执行:
- python main.py sandbox --dry-run
- python main.py sandbox
复制代码
检查sandbox分类结果后,再对真实目录先预览后执行。
七、常见错误与解决方案
1. 路径带空格被截断
命令参数中的空格会被解析为分隔符,导致只识别路径前半段。
解决办法:给路径加英文双引号。
2. PermissionError: [Errno 13]
文件正被PDF阅读器、播放器或下载进程占用,无法移动。
解决办法:关闭相关程序,或者等待下载完成后再执行。
脚本可以重复运行,因为已经移动的文件不在当前目录中,再次运行时会自动跳过。
3. 文件全部被归入“其他”
规则表中写的是"pdf"而实际item.suffix返回".pdf",导致查不到。
解决办法:确认规则表里的扩展名都带前导点。
4. 大写后缀无法识别,如.JPG全进“其他”
原因是没有调用suffix.lower()统一大小写。
解决方案:代码中已做lower()处理,如果自己调试,可以创建大写后缀文件在沙盒里测试。
5. 分类文件夹里混入.DS_Store
macOS会在文件夹中生成以点开头的系统文件,应跳过。
实际代码中通过item.name.startswith(".")判断,能同时过滤.DS_Store和desktop.ini这类隐藏文件。
6. 直接移动时目标文件重名造成的隐患
如果不使用unique_path,shutil.move在遇到同名目标时,不同OS下可能直接覆盖或抛错。
因此所有移动操作都应先经过unique_path处理。
八、适用场景与扩展练习
该脚本适合个人下载目录、临时文件目录的日常整理。
可以继续扩展:
- 在RULES中添加“电子书”或“代码”类别;
- 将默认行为改为只预览,增加--yes参数才真正执行;
- 用脚本批量创建20个假文件,验证每个分类的文件数量;
- 使用Windows任务计划程序或Mac launchd实现定时整理,但务必先在--dry-run模式下确认安全。
这个工具的价值在于安全地改变真实文件系统,分类规则独立、执行动作可预览、重名不覆盖,适合作为文件操作类Python脚本的模板。 |