Shell脚本如何查找重复文件并去重

wen 实用脚本 2

本文目录导读:

Shell脚本如何查找重复文件并去重

  1. 基于MD5校验的方法
  2. 保留一个副本删除其余
  3. 使用shasum(更准确的校验)
  4. 高级功能脚本
  5. 性能优化版本(适合大文件)
  6. 统计报告版本
  7. 使用建议

我来介绍几种在Shell脚本中查找和去重重复文件的方法:

基于MD5校验的方法

#!/bin/bash
# 方法1:查找重复文件并显示
find . -type f -exec md5sum {} \; | sort | uniq -d -w 32
# 方法2:显示重复文件的分组
find . -type f -exec md5sum {} \; | sort | awk '{
    if (seen[$1]) {
        print "Duplicate:", $2
    } else {
        seen[$1] = $2
    }
}'
# 方法3:完整的去重脚本
find_duplicates() {
    local search_dir="${1:-.}"
    echo "正在查找重复文件..."
    find "$search_dir" -type f -exec md5sum {} \; | 
    sort |
    awk '{
        if (seen[$1]) {
            print "DUPLICATE: " $2
            duplicates[$1]++
        } else {
            seen[$1] = $2
        }
    }'
    echo "统计重复文件数量..."
    awk '{print $1}' | sort | uniq -c | sort -rn
}

保留一个副本删除其余

#!/bin/bash
# 保留第一个文件,删除其他重复文件
dedup_keep_first() {
    local directory="${1:-.}"
    declare -A file_hashes
    find "$directory" -type f | while read -r file; do
        hash=$(md5sum "$file" | cut -d' ' -f1)
        if [[ -n "${file_hashes[$hash]}" ]]; then
            echo "删除重复文件: $file"
            rm "$file"
        else
            file_hashes[$hash]="$file"
        fi
    done
}
# 交互式去重(询问是否删除)
dedup_interactive() {
    find . -type f -exec md5sum {} \; | sort | awk '
    NR == 1 { first=$2; hash=$1 }
    NR > 1 {
        if ($1 == hash) {
            printf "保留: %s\n删除: %s [y/N] ", first, $2
            getline answer < "/dev/tty"
            if (answer == "y" || answer == "Y") {
                system("rm \"" $2 "\"")
                print "已删除"
            } else {
                print "保留"
                first=$2
            }
        } else {
            first=$2
            hash=$1
        }
    }'
}

使用shasum(更准确的校验)

#!/bin/bash
# 使用SHA256校验
find_duplicates_sha256() {
    find . -type f -exec sha256sum {} \; | sort | uniq -d -w 64 | while read -r hash file; do
        echo "重复文件: $file"
    done
}
# 查找并移动到回收站
dedup_to_trash() {
    local trash_dir="${HOME}/.trash"
    mkdir -p "$trash_dir"
    find . -type f -exec sha256sum {} \; | sort | awk '
    {
        if (seen[$1]) {
            dest="'"$trash_dir"'" "/" $2
            mkdir -p "$(dirname "$dest")"
            system("mv \"" $2 "\" \"" dest "\"")
            print "移动到回收站: " $2
        } else {
            seen[$1] = $2
        }
    }'
}

高级功能脚本

#!/bin/bash
# 完整的去重工具
dedup_tool() {
    local search_dir="${1:-.}"
    local action="${2:-list}"  # list, delete, move, hardlink
    local hash_cmd="${3:-md5sum}"
    local temp_file=$(mktemp)
    echo "扫描目录: $search_dir"
    echo "使用算法: $hash_cmd"
    echo "操作模式: $action"
    # 生成文件哈希列表
    find "$search_dir" -type f -exec "$hash_cmd" {} \; > "$temp_file"
    # 根据模式处理
    case "$action" in
        list)
            cat "$temp_file" | sort | awk '
            NR == 1 { first=$2; hash=$1 }
            NR > 1 {
                if ($1 == hash) {
                    print first
                    print $2
                } else {
                    first=$2
                    hash=$1
                }
            }' | sort -u
            ;;
        delete)
            cat "$temp_file" | sort | awk '
            {
                if (seen[$1]) {
                    print "删除: " $2
                    system("rm \"" $2 "\"")
                } else {
                    seen[$1] = $2
                }
            }'
            ;;
        move)
            local backup_dir="${search_dir}/duplicates_backup"
            mkdir -p "$backup_dir"
            cat "$temp_file" | sort | awk -v backup="$backup_dir" '
            {
                if (seen[$1]) {
                    dir=backup "/" $2
                    system("mkdir -p \"$(dirname \"" dir "\")\"")
                    system("mv \"" $2 "\" \"" dir "\"")
                    print "移动: " $2
                } else {
                    seen[$1] = $2
                }
            }'
            ;;
        hardlink)
            # 使用硬链接节省空间
            cat "$temp_file" | sort | awk '
            {
                if (seen[$1]) {
                    system("ln -f \"" seen[$1] "\" \"" $2 "\"")
                    print "硬链接: " $2 " -> " seen[$1]
                } else {
                    seen[$1] = $2
                }
            }'
            ;;
    esac
    rm -f "$temp_file"
}
# 使用方法
# dedup_tool "/path/to/dir" "list" "sha256sum"
# dedup_tool "/path/to/dir" "delete" "md5sum"
# dedup_tool "/path/to/dir" "hardlink" "sha1sum"

性能优化版本(适合大文件)

#!/bin/bash
# 快速去重(先按文件大小过滤)
dedup_fast() {
    local directory="${1:-.}"
    echo "步骤1: 按文件大小分组..."
    find "$directory" -type f -exec stat -f"%z %N" {} \; | sort -n | awk '
    {
        if (size != $1) {
            size=$1
            files=$2
        } else {
            print files
            print $2
        }
    }' | while read -r file1 && read -r file2; do
        # 只对大小相同的文件进行哈希比较
        if [ -f "$file1" ] && [ -f "$file2" ]; then
            if cmp -s "$file1" "$file2"; then
                echo "重复文件: $file1 和 $file2"
            fi
        fi
    done
}

统计报告版本

#!/bin/bash
# 生成重复文件报告
report_duplicates() {
    local directory="${1:-.}"
    local report_file="duplicates_report_$(date +%Y%m%d).txt"
    {
        echo "重复文件报告"
        echo "生成时间: $(date)"
        echo "扫描目录: $directory"
        echo "========================================"
        echo ""
        find "$directory" -type f -exec sha256sum {} \; | sort | awk '
        {
            if (seen[$1]) {
                if (!header_printed[$1]) {
                    print "哈希: " $1
                    print "  原始: " seen[$1]
                    header_printed[$1] = 1
                }
                print "  重复: " $2
                count[$1]++
            } else {
                seen[$1] = $2
            }
        }
        END {
            print ""
            print "统计信息:"
            for (hash in count) {
                print "  " count[hash] "个重复文件 (哈希: " substr(hash,1,8) "...)"
            }
        }'
    } > "$report_file"
    echo "报告已保存到: $report_file"
    wc -l "$report_file"
}

使用建议

  1. 先测试:在正式删除前先用 list 模式查看结果
  2. 备份重要文件:使用 move 模式移动重复文件
  3. 大文件处理:先按大小过滤,再比较校验码
  4. 软链接保留:如需保留软链接,使用 -L 参数
  5. 空格处理:确保文件名含空格时正确引用
  6. 权限问题:可能需要 sudo 权限访问某些文件

这些脚本可以根据实际需求进行调整和组合使用。

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