轻轻一点,重复文件无处遁形,安全高效地释放数十GB存储空间。
你是否也常常在Mac上遇到这样的困扰:下载的文件重复保存,照片库中充斥着类似图片,文档多个版本混乱不堪?不知不觉间,重复文件蚕食了你宝贵的存储空间。
今天,我们为Mac用户带来一个安全、智能、高效的重复文件清理解决方案!无需安装臃肿的清理软件,只需几行Python代码,就能让你的Mac重获新生。
一键式智能清理脚本
第一,确保你的Mac已安装Python 3。打开终端,输入以下命令安装所需工具:
pip3 install send2trash
然后,创建我们的智能清理脚本 clean_duplicates.py:
#!/usr/bin/env python3
"""
Mac重复文件安全清理工具
安全、智能、高效,释放你的存储空间
"""
import os
import time
import hashlib
import json
from pathlib import Path
import send2trash
from datetime import datetime
class MacDuplicateCleaner:
"""Mac专用重复文件清理器"""
def __init__(self):
self.file_hashes = {}
self.duplicates = []
self.scanned_files = 0
self.start_time = time.time()
def get_file_fingerprint(self, file_path, use_quick_hash=True):
"""
获取文件指纹:大小 + 部分哈希
兼顾准确性和性能
"""
try:
file_size = os.path.getsize(file_path)
if use_quick_hash:
# 快速模式:读取文件前1MB计算哈希
with open(file_path, 'rb') as f:
# 小文件直接读取全部,大文件读取前1MB
sample = f.read(1024 * 1024) if file_size > 1024 * 1024 else f.read()
if sample:
return f"{file_size}_{hashlib.md5(sample).hexdigest()}"
else:
# 准确模式:完整文件哈希
hash_md5 = hashlib.md5()
with open(file_path, 'rb') as f:
for chunk in iter(lambda: f.read(8192), b""):
hash_md5.update(chunk)
return f"{file_size}_{hash_md5.hexdigest()}"
except (PermissionError, OSError, IOError):
return None
def scan_directory(self, directory, extensions=None, min_size_kb=10):
"""
扫描目录查找重复文件
"""
directory = os.path.expanduser(directory)
if not os.path.exists(directory):
print(f"⚠️ 目录不存在: {directory}")
return
print(f" 正在扫描: {directory}")
for root, dirs, files in os.walk(directory):
# 跳过系统隐藏目录
dirs[:] = [d for d in dirs if not d.startswith('.')]
for file in files:
# 跳过隐藏文件和系统文件
if file.startswith('.') or file.startswith('._'):
continue
file_path = os.path.join(root, file)
# 过滤文件类型
if extensions:
if not any(file.lower().endswith(ext) for ext in extensions):
continue
# 过滤小文件
try:
file_size_kb = os.path.getsize(file_path) / 1024
if file_size_kb < min_size_kb:
continue
except OSError:
continue
# 获取文件指纹
fingerprint = self.get_file_fingerprint(file_path)
if fingerprint is None:
continue
self.scanned_files += 1
# 检查是否重复
if fingerprint in self.file_hashes:
original_file = self.file_hashes[fingerprint]
self.duplicates.append({
'original': original_file,
'duplicate': file_path,
'size_kb': file_size_kb,
'fingerprint': fingerprint
})
print(f" 发现重复: {file}")
else:
self.file_hashes[fingerprint] = file_path
# 显示进度
if self.scanned_files % 100 == 0:
elapsed = time.time() - self.start_time
print(f" 已扫描 {self.scanned_files} 个文件,耗时 {elapsed:.1f}秒")
def smart_review_duplicates(self):
"""
智能审查重复文件
"""
if not self.duplicates:
print(" 祝贺!未发现重复文件")
return
print(f"
扫描完成!发现 {len(self.duplicates)} 个重复文件")
print("=" * 60)
total_size_mb = sum(d['size_kb'] for d in self.duplicates) / 1024
# 按文件类型分组
file_groups = {}
for dup in self.duplicates:
ext = os.path.splitext(dup['duplicate'])[1].lower() or '无扩展名'
if ext not in file_groups:
file_groups[ext] = []
file_groups[ext].append(dup)
print("
重复文件分类统计:")
for ext, files in sorted(file_groups.items(), key=lambda x: len(x[1]), reverse=True):
group_size = sum(f['size_kb'] for f in files) / 1024
print(f" {ext}: {len(files)} 个文件,约 {group_size:.1f} MB")
print(f"
可释放空间: {total_size_mb:.1f} MB")
return total_size_mb
def interactive_cleanup(self):
"""
交互式清理重复文件
"""
if not self.duplicates:
return
print("
️ 请选择清理模式:")
print(" 1. 自动模式(保留最新修改的文件)")
print(" 2. 手动选择(逐个确认)")
print(" 3. 仅查看,不删除")
choice = input("请输入选项 (1/2/3): ").strip()
removed_count = 0
saved_space = 0
if choice == '1':
# 自动模式:保留最新修改的文件
for dup in self.duplicates:
try:
time_original = os.path.getmtime(dup['original'])
time_duplicate = os.path.getmtime(dup['duplicate'])
# 删除较旧的文件
file_to_remove = dup['duplicate'] if time_duplicate < time_original else dup['original']
print(f"️ 删除: {os.path.basename(file_to_remove)}")
send2trash.send2trash(file_to_remove)
removed_count += 1
saved_space += dup['size_kb']
except Exception as e:
print(f"❌ 删除失败: {e}")
elif choice == '2':
# 手动模式
for i, dup in enumerate(self.duplicates, 1):
print(f"
重复组 {i}/{len(self.duplicates)}:")
print(f" A: {dup['original']}")
print(f" 大小: {dup['size_kb']:.1f} KB")
print(f" 修改时间: {datetime.fromtimestamp(os.path.getmtime(dup['original']))}")
print(f"
B: {dup['duplicate']}")
print(f" 大小: {dup['size_kb']:.1f} KB")
print(f" 修改时间: {datetime.fromtimestamp(os.path.getmtime(dup['duplicate']))}")
action = input("
请选择 (A保留/B保留/跳过): ").strip().upper()
if action == 'A':
try:
send2trash.send2trash(dup['duplicate'])
print("✅ 已删除B文件")
removed_count += 1
saved_space += dup['size_kb']
except Exception as e:
print(f"❌ 删除失败: {e}")
elif action == 'B':
try:
send2trash.send2trash(dup['original'])
print("✅ 已删除A文件")
removed_count += 1
saved_space += dup['size_kb']
except Exception as e:
print(f"❌ 删除失败: {e}")
else:
print("⏭️ 跳过")
if removed_count > 0:
saved_mb = saved_space / 1024
print(f"
✨ 清理完成!")
print(f" 删除文件数: {removed_count}")
print(f" 释放空间: {saved_mb:.1f} MB")
print(f" 总耗时: {time.time() - self.start_time:.1f}秒")
def save_report(self, filename="duplicates_report.json"):
"""
保存扫描报告
"""
report = {
'scan_time': datetime.now().isoformat(),
'scanned_files': self.scanned_files,
'duplicates_found': len(self.duplicates),
'total_size_mb': sum(d['size_kb'] for d in self.duplicates) / 1024,
'duplicates': self.duplicates
}
with open(filename, 'w', encoding='utf-8') as f:
json.dump(report, f, ensure_ascii=False, indent=2)
print(f"
报告已保存: {filename}")
def main():
"""
主函数:提供用户友善的清理体验
"""
print("""
Mac重复文件清理助手
======================
安全 · 高效 · 智能
特点:
✅ 安全删除(移至废纸篓)
✅ 智能识别(大小+哈希双重验证)
✅ 多格式支持(图片、文档、压缩包等)
✅ 进度显示,随时掌控
""")
cleaner = MacDuplicateCleaner()
# 让用户选择扫描目录
print("
请选择扫描目录:")
print(" 1. 下载文件夹 (~/Downloads)")
print(" 2. 桌面 (~/Desktop)")
print(" 3. 文档 (~/Documents)")
print(" 4. 图片 (~/Pictures)")
print(" 5. 自定义目录")
dir_choice = input("请选择 (1-5): ").strip()
directories = {
'1': '~/Downloads',
'2': '~/Desktop',
'3': '~/Documents',
'4': '~/Pictures',
'5': input("请输入目录路径: ").strip()
}
target_dir = directories.get(dir_choice, '~/Downloads')
# 选择文件类型
print("
选择文件类型:")
print(" 1. 所有类型")
print(" 2. 图片 (.jpg, .png, .heic)")
print(" 3. 文档 (.pdf, .doc, .ppt, .xls)")
print(" 4. 压缩包 (.zip, .rar, .7z)")
print(" 5. 视频 (.mp4, .mov, .avi)")
type_choice = input("请选择 (1-5): ").strip()
extensions_map = {
'1': None,
'2': ['.jpg', '.jpeg', '.png', '.gif', '.heic', '.tiff', '.bmp'],
'3': ['.pdf', '.doc', '.docx', '.ppt', '.pptx', '.xls', '.xlsx', '.txt', '.rtf'],
'4': ['.zip', '.rar', '.7z', '.tar', '.gz'],
'5': ['.mp4', '.mov', '.avi', '.mkv', '.flv', '.wmv']
}
extensions = extensions_map.get(type_choice)
# 设置最小文件大小
print("
设置最小文件大小(默认10KB):")
min_size = input("最小文件大小(KB,回车使用默认值10): ").strip()
min_size_kb = int(min_size) if min_size.isdigit() else 10
# 开始扫描
print("
" + "="*60)
print("开始扫描...")
print("="*60)
cleaner.scan_directory(
directory=target_dir,
extensions=extensions,
min_size_kb=min_size_kb
)
# 显示结果并清理
cleaner.smart_review_duplicates()
cleaner.interactive_cleanup()
# 保存报告
save_report = input("
是否保存扫描报告?(y/n): ").strip().lower()
if save_report == 'y':
cleaner.save_report()
if __name__ == "__main__":
main()
三大核心功能,彻底解决重复文件问题
1.智能识别技术
我们的脚本采用双重验证机制:
- 文件大小比对(快速筛选)
- 内容哈希校验(准确匹配)
即使是修改时间不同、但内容完全一样的文件,也逃不过它的”法眼”。
2.安全删除保障
使用 send2trash 库,所有删除操作都只是将文件移至废纸篓,而非永久删除。如果误删重大文件,随时可以从废纸篓恢复。
3.智能清理策略
脚本提供多种清理模式:
- 自动模式:保留最新修改的文件
- 手动模式:逐个文件确认
- 预览模式:仅查看不删除
简易操作
运行代码后,根据需要选择功能即可:
- 选择扫描目录(下载、桌面、文档等)
- 选择文件类型(图片、文档、视频等)
- 设置最小文件大小
- 查看扫描结果
- 选择清理方式
️ 安全保障措施
- 只扫描用户目录:绝不触碰系统文件
- 废纸篓删除:所有删除都可恢复
- 人工确认:重大文件需要你亲自确认
- 扫描报告:完整记录所有操作
批量处理多个目录
修改代码中的扫描部分,可以同时处理多个文件夹:
python
# 同时扫描下载和文档文件夹
cleaner.scan_directory('~/Downloads')
cleaner.scan_directory('~/Documents')
定时自动清理
使用macOS的launchd设置每周自动清理:
bash
# 创建plist文件 ~/Library/LaunchAgents/com.user.clean-duplicates.plist
# 设置每周日凌晨3点自动运行
排除特定文件夹
在扫描时跳过某些目录:
python
# 在scan_directory方法中添加
if 'node_modules' in root or '.git' in root:
dirs[:] = [] # 跳过此目录
注意事项
- 首次使用提议选择”预览模式”,确认无误后再清理
- 清理前确保废纸篓有足够空间
- 重大文件提议先备份到云端
- 系统升级前不要进行大规模清理
还在为重复文件烦恼吗?立即复制上面的代码,给你的Mac来一次深度清洁吧!记得先运行预览模式,确认无误后再进行清理操作。
释放存储空间,提升工作效率,从清理重复文件开始!
温馨提示:定期清理是好习惯,但重大文件请务必做好备份。技术虽好,数据无价!
记得收藏这篇文章,方便后来随时使用!也欢迎分享给需要的朋友~
#Mac技巧 #电脑清理 #存储空间 #Python #效率工具
互动话题:你的Mac还剩多少存储空间?用这个方法清理出了多少空间?欢迎在评论区分享你的成果!
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...




