脚本如何检测重复文件夹

wen 实用脚本 28

本文目录导读:

脚本如何检测重复文件夹

  1. 基于文件内容哈希的精确检测
  2. 基于文件元数据的快速检测
  3. Shell 脚本检测
  4. 使用 fdupes 工具(快速)
  5. Windows PowerShell 脚本
  6. 性能优化版本
  7. 使用建议

我来介绍几种检测重复文件夹的方法,包括不同的实现方案:

基于文件内容哈希的精确检测

Python 实现

import os
import hashlib
from collections import defaultdict
def get_folder_hash(folder_path):
    """计算文件夹的哈希值(基于所有文件内容)"""
    sha256_hash = hashlib.sha256()
    # 遍历所有文件(按排序保持一致性)
    for root, dirs, files in os.walk(folder_path):
        dirs.sort()
        files.sort()
        for file_name in files:
            file_path = os.path.join(root, file_name)
            # 添加相对路径信息
            rel_path = os.path.relpath(file_path, folder_path)
            sha256_hash.update(rel_path.encode())
            # 添加文件内容
            with open(file_path, "rb") as f:
                for chunk in iter(lambda: f.read(4096), b""):
                    sha256_hash.update(chunk)
    return sha256_hash.hexdigest()
def find_duplicate_folders(root_path):
    """查找重复文件夹"""
    folder_hashes = defaultdict(list)
    # 遍历所有子文件夹
    for item in os.listdir(root_path):
        item_path = os.path.join(root_path, item)
        if os.path.isdir(item_path):
            try:
                folder_hash = get_folder_hash(item_path)
                folder_hashes[folder_hash].append(item_path)
            except Exception as e:
                print(f"Error processing {item_path}: {e}")
    # 返回重复的文件夹
    duplicates = {hash_val: paths for hash_val, paths in folder_hashes.items() 
                  if len(paths) > 1}
    return duplicates
# 使用示例
root = "/path/to/search"
duplicates = find_duplicate_folders(root)
for hash_val, paths in duplicates.items():
    print(f"Duplicate folders ({len(paths)} copies):")
    for path in paths:
        print(f"  - {path}")

基于文件元数据的快速检测

import os
from collections import defaultdict
def get_folder_signature(folder_path):
    """快速获取文件夹特征(文件数量、大小等)"""
    total_files = 0
    total_size = 0
    structure = []
    for root, dirs, files in os.walk(folder_path):
        dirs.sort()
        files.sort()
        # 记录相对路径结构
        rel_path = os.path.relpath(root, folder_path)
        structure.append({
            'path': rel_path,
            'files': files,
            'dirs': dirs
        })
        total_files += len(files)
        for file_name in files:
            file_path = os.path.join(root, file_name)
            try:
                total_size += os.path.getsize(file_path)
            except:
                pass
    return {
        'total_files': total_files,
        'total_size': total_size,
        'structure_hash': hash(str(structure))
    }
def find_duplicate_folders_fast(root_path):
    """快速查找可能的重复文件夹"""
    folder_signatures = defaultdict(list)
    for item in os.listdir(root_path):
        item_path = os.path.join(root_path, item)
        if os.path.isdir(item_path):
            sig = get_folder_signature(item_path)
            folder_signatures[sig['structure_hash']].append(item_path)
    # 只返回可能的重复项
    potential_dupes = {k: v for k, v in folder_signatures.items() if len(v) > 1}
    return potential_dupes

Shell 脚本检测

#!/bin/bash
# 基于文件列表的重复检测
function find_duplicate_dirs() {
    local search_path="$1"
    # 获取所有子目录的文件列表
    for dir in "$search_path"/*/; do
        if [ -d "$dir" ]; then
            dir_name=$(basename "$dir")
            # 生成文件列表(排序后)
            file_list=$(find "$dir" -type f -exec basename {} \; | sort | md5sum)
            # 使用关联数组存储
            echo "$dir_name|$file_list"
        fi
    done | sort -t'|' -k2 | awk -F'|' '
        {
            if ($2 == prev_hash) {
                print "DUPLICATE: " prev_dir " <=> " $1
            } else {
                prev_hash = $2
                prev_dir = $1
            }
        }
    '
}
# 使用示例
find_duplicate_dirs "/path/to/check"

使用 fdupes 工具(快速)

import subprocess
import json
def find_duplicates_with_fdupes(directory, recurse=True):
    """使用 fdupes 工具检测重复文件,然后分析文件夹"""
    cmd = ['fdupes', '-r', '-S', directory]
    result = subprocess.run(cmd, capture_output=True, text=True)
    # 解析输出,分组重复文件
    duplicate_groups = []
    current_group = []
    for line in result.stdout.split('\n'):
        if line.strip() == '':
            if current_group:
                duplicate_groups.append(current_group)
                current_group = []
        else:
            current_group.append(line.strip())
    # 分析哪些文件夹包含重复文件
    folder_duplicates = defaultdict(set)
    for group in duplicate_groups:
        if len(group) > 1:
            for file_path in group:
                folder = os.path.dirname(file_path)
                # 记录重复文件对
                for other_file in group:
                    if other_file != file_path:
                        folder_duplicates[folder].add(other_file)
    return folder_duplicates

Windows PowerShell 脚本

# 查找重复文件夹的PowerShell脚本
function Find-DuplicateFolders {
    param(
        [string]$Path,
        [switch]$Recurse
    )
    $folderHashes = @{}
    # 获取所有子文件夹
    $folders = Get-ChildItem -Path $Path -Directory -Recurse:$Recurse
    foreach ($folder in $folders) {
        $hash = New-Object System.Security.Cryptography.SHA256Managed
        # 获取文件夹中所有文件
        $files = Get-ChildItem -Path $folder.FullName -File -Recurse | Sort-Object Name
        foreach ($file in $files) {
            $stream = [System.IO.File]::OpenRead($file.FullName)
            $hash.ComputeHash($stream) | Out-Null
            $stream.Close()
        }
        $hashString = [System.BitConverter]::ToString($hash.Hash) -replace '-', ''
        if (-not $folderHashes.ContainsKey($hashString)) {
            $folderHashes[$hashString] = @()
        }
        $folderHashes[$hashString] += $folder.FullName
    }
    # 只返回重复项
    $folderHashes.GetEnumerator() | Where-Object { $_.Value.Count -gt 1 }
}
# 使用示例
Find-DuplicateFolders -Path "C:\MyFolder" -Recurse

性能优化版本

import os
import hashlib
from concurrent.futures import ThreadPoolExecutor, as_completed
def hash_file_quick(file_path):
    """快速哈希单个文件"""
    h = hashlib.md5()
    with open(file_path, 'rb') as f:
        # 只读取前4KB和最后4KB作为快速比较
        first_chunk = f.read(4096)
        h.update(first_chunk)
        f.seek(-4096, 2)
        last_chunk = f.read(4096)
        h.update(last_chunk)
    return h.hexdigest()
def compare_folders_parallel(folder1, folder2):
    """并行比较两个文件夹"""
    def get_files_structure(path):
        """获取文件结构"""
        files = []
        for root, dirs, fnames in os.walk(path):
            for fname in fnames:
                fpath = os.path.join(root, fname)
                rel_path = os.path.relpath(fpath, path)
                files.append((rel_path, fpath))
        return sorted(files, key=lambda x: x[0])
    files1 = get_files_structure(folder1)
    files2 = get_files_structure(folder2)
    # 快速大小和数量检查
    if len(files1) != len(files2):
        return False
    # 并行哈希比较
    with ThreadPoolExecutor(max_workers=4) as executor:
        futures = []
        for i, ((rel1, path1), (rel2, path2)) in enumerate(zip(files1, files2)):
            if rel1 != rel2:
                return False
            futures.append(executor.submit(hash_file_quick, path1))
            futures.append(executor.submit(hash_file_quick, path2))
        # 验证哈希
        hashes = []
        for future in as_completed(futures):
            hashes.append(future.result())
        # 分组比较
        for i in range(0, len(hashes), 2):
            if hashes[i] != hashes[i+1]:
                return False
    return True

使用建议

  1. 选择合适的方法

    • 精确检测:使用完整的文件哈希
    • 快速检测:使用文件数量和大小
    • 大规模检测:使用并行处理
  2. 性能优化

    • 先比较文件数量和总大小
    • 使用快速哈希(MD5而非SHA256)
    • 只读取部分文件内容
  3. 注意事项

    • 处理权限问题
    • 考虑符号链接
    • 排除系统文件
    • 处理大文件时的内存管理

根据具体需求选择合适的实现方案即可。

抱歉,评论功能暂时关闭!