群晖删除重复文件(python方法)
因为我的群晖型号是DS218+没有照片去重功能,加上自己工作电脑太多,经常重复备份很多资料。看过网上很多删除重复文件方法,试过,比较麻烦而且删不干净!我估计自己电脑重复文件,最少3W个以上。
思路:
找出重复文件 --> 提取重复文件路径 --> 用python程序读出路径并且删除文件
具体步骤:
1、存储空间分析器
使用存储空间分析器,新建任务;

立即生成报告并且查看报告

下载报告并且提取路径

excel当中使用IF条件,筛选出重复

使用筛选功能提取重复




保存为1.txt 此文件保存为重复文件的路径;我保存在home目录下面,
对应为/volume1/homes/xudada/1.txt(第一部分结束了)
2、配置NAS环境


在开源里面找到python并且安装
在社群里面找到terminal并且安装

启动terminal的时候需要权限。(注意会弹出窗口,记住上面的命令)
sudo sed -i 's/package/root/g' /var/packages/terminal/conf/privilege

使用secureCRT远程连接NAS,设置权限;





3、python代码部分
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
群晖重复文件删除工具
安全删除文本文件中列出的文件,并显示详细的删除动作
"""
import os
import sys
import time
import shutil
import argparse
from pathlib import Path
from typing import List, Tuple
class FileDeleter:
"""文件删除器类"""
def __init__(self, list_file: str, simulate: bool = False, verbose: bool = False):
"""
初始化文件删除器
Args:
list_file: 包含文件路径的文本文件
simulate: 模拟模式,只显示不实际删除
verbose: 详细模式,显示更多信息
"""
self.list_file = Path(list_file).resolve()
self.simulate = simulate
self.verbose = verbose
self.deleted_count = 0
self.skipped_count = 0
self.error_count = 0
self.total_size = 0
# 颜色代码(在终端中显示颜色)
self.COLORS = {
'RED': '\033[91m',
'GREEN': '\033[92m',
'YELLOW': '\033[93m',
'BLUE': '\033[94m',
'MAGENTA': '\033[95m',
'CYAN': '\033[96m',
'RESET': '\033[0m',
'BOLD': '\033[1m',
}
def color_text(self, text: str, color: str) -> str:
"""为文本添加颜色"""
if sys.stdout.isatty():
return f"{self.COLORS.get(color, '')}{text}{self.COLORS['RESET']}"
return text
def print_header(self):
"""打印程序头信息"""
print(self.color_text("=" * 70, "BLUE"))
print(self.color_text("群晖重复文件删除工具", "BOLD") + self.color_text(" v1.0", "YELLOW"))
print(self.color_text("=" * 70, "BLUE"))
print(f"文件列表: {self.color_text(str(self.list_file), 'CYAN')}")
print(f"模式: {self.color_text('模拟运行' if self.simulate else '实际删除', 'YELLOW')}")
print(f"开始时间: {time.strftime('%Y-%m-%d %H:%M:%S')}")
print(self.color_text("-" * 70, "BLUE"))
def print_footer(self):
"""打印程序尾信息"""
print(self.color_text("-" * 70, "BLUE"))
print(f"完成时间: {time.strftime('%Y-%m-%d %H:%M:%S')}")
print(self.color_text("=" * 70, "BLUE"))
# 统计信息
total_files = self.deleted_count + self.skipped_count + self.error_count
print(f"\n{self.color_text('删除统计:', 'BOLD')}")
print(f"总文件数: {total_files}")
print(f"已删除: {self.color_text(str(self.deleted_count), 'GREEN')}")
print(f"已跳过: {self.color_text(str(self.skipped_count), 'YELLOW')}")
print(f"错误: {self.color_text(str(self.error_count), 'RED')}")
if self.total_size > 0:
size_str = self.format_size(self.total_size)
print(f"释放空间: {self.color_text(size_str, 'GREEN')}")
print(self.color_text("=" * 70, "BLUE"))
def format_size(self, size_bytes: int) -> str:
"""格式化文件大小"""
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if size_bytes < 1024.0:
return f"{size_bytes:.2f} {unit}"
size_bytes /= 1024.0
return f"{size_bytes:.2f} PB"
def clean_file_list(self) -> List[str]:
"""
清理和验证文件列表
Returns:
清理后的有效文件路径列表
"""
if not self.list_file.exists():
print(self.color_text(f"错误: 文件列表 '{self.list_file}' 不存在!", "RED"))
sys.exit(1)
print(f"正在读取文件列表: {self.color_text(str(self.list_file), 'CYAN')}")
cleaned_paths = []
with open(self.list_file, 'r', encoding='utf-8', errors='ignore') as f:
for line_num, line in enumerate(f, 1):
# 清理行:去除首尾空白字符,包括换行符
path_str = line.strip()
# 跳过空行和注释行
if not path_str or path_str.startswith('#'):
continue
# 处理相对路径:转换为绝对路径
path = Path(path_str)
if not path.is_absolute():
# 相对于文件列表所在目录
path = self.list_file.parent / path
# 解析符号链接
try:
if path.is_symlink():
real_path = path.resolve()
if self.verbose:
print(f"行{line_num}: 符号链接 {path} -> {real_path}")
path = real_path
except:
pass # 无法解析符号链接,保持原路径
cleaned_paths.append(str(path))
print(f"找到 {len(cleaned_paths)} 个文件路径")
return cleaned_paths
def delete_file(self, file_path: str) -> Tuple[bool, str, int]:
"""
删除单个文件
Args:
file_path: 要删除的文件路径
Returns:
(成功标志, 消息, 文件大小)
"""
path = Path(file_path)
# 检查文件是否存在
if not path.exists():
return False, "文件不存在", 0
# 检查是否是文件(不是目录)
if not path.is_file():
return False, "不是常规文件(可能是目录)", 0
# 获取文件大小
try:
file_size = path.stat().st_size
except:
file_size = 0
# 检查权限
if not os.access(str(path), os.W_OK):
return False, "没有写入权限", file_size
# 执行删除
try:
if self.simulate:
return True, "模拟删除成功", file_size
else:
path.unlink()
return True, "删除成功", file_size
except Exception as e:
return False, f"删除失败: {str(e)}", file_size
def process_files(self, file_paths: List[str]):
"""处理所有文件"""
print(f"\n{self.color_text('开始处理文件:', 'BOLD')}")
for i, file_path in enumerate(file_paths, 1):
# 显示处理进度
progress = f"[{i}/{len(file_paths)}]"
# 删除文件
success, message, file_size = self.delete_file(file_path)
# 更新统计
if success:
self.deleted_count += 1
self.total_size += file_size
status_color = "GREEN"
status_mark = "✓"
else:
if "不存在" in message or "不是常规文件" in message:
self.skipped_count += 1
status_color = "YELLOW"
status_mark = "○"
else:
self.error_count += 1
status_color = "RED"
status_mark = "✗"
# 显示结果
file_display = file_path
if len(file_display) > 60:
file_display = "..." + file_display[-57:]
status = self.color_text(status_mark, status_color)
size_info = f" ({self.format_size(file_size)})" if file_size > 0 else ""
print(f"{progress} {status} {file_display}{size_info}")
if self.verbose and message:
print(f" {self.color_text(message, status_color)}")
def run(self):
"""运行删除程序"""
self.print_header()
# 清理和获取文件列表
file_paths = self.clean_file_list()
if not file_paths:
print(self.color_text("没有找到有效的文件路径!", "YELLOW"))
self.print_footer()
return
# 确认操作(非模拟模式时)
if not self.simulate:
print(f"\n{self.color_text('警告:', 'RED')} 即将删除 {len(file_paths)} 个文件!")
confirm = input(f"{self.color_text('是否继续? (y/N): ', 'YELLOW')}")
if confirm.lower() != 'y':
print(self.color_text("操作已取消", "YELLOW"))
return
# 处理文件
self.process_files(file_paths)
# 显示统计信息
self.print_footer()
def main():
"""主函数"""
parser = argparse.ArgumentParser(
description="群晖重复文件删除工具 - 安全删除文本文件中列出的文件",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
使用示例:
%(prog)s filelist.txt # 实际删除文件
%(prog)s filelist.txt --simulate # 模拟运行,不实际删除
%(prog)s filelist.txt --verbose # 详细输出模式
文件列表格式:
每行一个文件路径,支持绝对路径或相对路径
空行和以#开头的行会被忽略
"""
)
parser.add_argument(
"list_file",
help="包含要删除文件路径的文本文件"
)
parser.add_argument(
"-s", "--simulate",
action="store_true",
help="模拟模式,显示将要删除的文件但不实际执行"
)
parser.add_argument(
"-v", "--verbose",
action="store_true",
help="详细输出模式"
)
parser.add_argument(
"-q", "--quiet",
action="store_true",
help="安静模式,只显示错误信息"
)
args = parser.parse_args()
# 设置输出级别
if args.quiet:
sys.stdout = open(os.devnull, 'w')
try:
# 创建删除器并运行
deleter = FileDeleter(
list_file=args.list_file,
simulate=args.simulate,
verbose=args.verbose
)
deleter.run()
except KeyboardInterrupt:
print(f"\n{deleter.color_text('操作被用户中断', 'YELLOW')}")
sys.exit(1)
except Exception as e:
print(f"\n{deleter.color_text(f'程序错误: {str(e)}', 'RED')}")
sys.exit(1)
if __name__ == "__main__":
main()
将python代码保存为file_deleter.py(路径依然为home)
在nas中使用文本编辑器保存,命名为file_deleter.py
在terminal当中为file_deleter.py赋予权限

python3 file_deleter.py /volume1/homes/xudada/1.txt


输入Y进行下一步



魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)