通过递归方式与 MD5 进行比较来删除重复文件

通过递归方式与 MD5 进行比较来删除重复文件

我想根据 MD5 值删除重复文件。我已经获得了下面的脚本,但我该如何修改它以使其以递归方式工作?

例如我有一个包含 3 个子文件夹 A B C 的文件夹

我希望检查 ./ ./A/ ./B/ ./C/ 中的所有文件的 md5 并相互比较,如果发现匹配,则随机删除任一匹配。最后不再存在重复项。我不关心哪个匹配项先被删除。

我希望我已经足够清楚地表达了我需要实现的目标,如果没有,请告诉我:)

#!/bin/bash
while true
do
  echo "Enter the directory:"
  read directory
  if [ -d $directory ]; then
    break
  else
    echo "Invalid directory"
  fi
done

for FILE in `ls $directory`
do
  if [ ! -f $FILE ]; then
    break;
  fi
  h=`md5sum $directory/$FILE | awk '{ print $1 }'`
  for f in `ls $directory`
  do
    if [ -f $f ] && [ $FILE != $f ]; then
      s=`md5sum $directory/$f | awk '{ print $1 }'`
      if [ "$s" = "$h" ]; then
        echo Removing $f
        rm -rf $directory/$f
      fi
    fi
  done
done

答案1

我建议采取如下措施:

find . -type f \
    | xargs md5sum \
    | sort -k1,1 \
    | uniq -Dw32

这将列出具有相同 MD5 哈希值的文件组中的所有重复文件。

注意,因为-w32参数uniq只会比较前 32 个字符……如果您更改哈希的长度,则需要更新它。


考虑下面的树,其内容如下:

./a/1: foo
./a/2: bar
./b/3: hello world
./b/d/5: bar
./c/4: foo
$ find . -type f \
>     | xargs md5sum \
>     | sort -k1,1 \
>     | uniq -Dw32
c157a79031e1c40f85931829bc5fc552  ./a/2
c157a79031e1c40f85931829bc5fc552  ./b/d/5
d3b07384d113edec49eaa6238ad5ff00  ./a/1
d3b07384d113edec49eaa6238ad5ff00  ./c/4

您现在可以逐行处理这些行...每行前面都有匹配的哈希值,指向一个可以进行重复数据删除的文件。

如果你不太在意哪个文件被删除,那么类似这样的操作就会起作用:

find . -type f \
    | xargs md5sum \
    | sort -k1,1 \
    | uniq -Dw32 \
    | while read hash file; do 
        [ "${prev_hash}" == "${hash}" ] && rm -v "${file}"
        prev_hash="${hash}"; 
    done

注意MD5 不再被认为是安全的... 因此,如果您在用户控制文件的系统中使用此方法,那么他们就有可能设计冲突 - 因此您可能会意外删除合法/目标文件,而不是像您希望的那样进行重复数据删除。更喜欢更强大的哈希,例如SHA-256

答案2

首先要提醒的是:基于校验和假设身份非常危险。不推荐。

使用校验和作为过滤器来删除确定的非重复项是可以的。

如果我要这么做,我会这样做:

  1. 根据长度创建文件列表(长度,完整路径名)

  2. 扫描该列表寻找潜在的重复长度。

  3. 任何比赛潜在的重复,如果可能的话,我会正确比较可疑文件。

使用长度的原因是,该信息可以非常快速地获得,而无需逐字节扫描文件,因为它通常在文件系统统计信息中以便快速访问。

如果您认为这比直接比较文件更快,可以使用类似的方法(计算一次校验和)添加另一个阶段来比较校验和(针对相似长度的文件)。使用类似的方法(从匹配的长度列表开始并计算这些文件的校验和)。

仅当存在多个长度相同的文件时,进行校验和计算才会对您有益,即使如此,直接逐字节比较也可能会很快发现不匹配的内容。

答案3

有一个漂亮的解决方案https://stackoverflow.com/questions/57736996/how-to-remove-duplicate-files-in-linux/57737192#57737192

md5sum prime-* | awk 'n[$1]++' | cut -d " " -f 3- | xargs -I {} echo rm {}

答案4

进入您想要检查的文件夹,列出文件并检查所有文件,如果 md5 匹配并且文件名不同,则建议删除该文件。

下面的脚本正是这样做的。请记住,这是一个模板,它会吐出所有文件名和校验和以用于调试目的,它实际上并没有删除,而是回显了您可以删除的文件名。

根据您的需要进行编辑。

#!/bin/bash

function getone(){
h=$(md5sum "${a}" | awk '{print $1}')   
}

function gettwo(){
s=$(md5sum "${x}" | awk '{print $1}')
}

echo "Type the directory NAME"
read directory

if [ -d ${directory} ]
then cd ${directory}
    for a in *.*
        do echo checking "${a}"
        getone
        echo $h # irrelevant echo, just for debug, you can remove it
            for x in *.*
            do echo scanning "${x}" # irrelevant echo, just for debug, you can remove it
            gettwo
            echo $s # irrelevant echo, just for debug, you can remove it
                if [ "${a}" = "${x}" ]
                then echo "Original file, skipping" # irrelevant echo, just for debug, you can remove it by leaving empty quotes.
                elif [ "${h}" = "${s}" ]
                then echo "Delete ${x}"  # This should be replaced by rm once you are happy with the script
                fi
            done
        done
else echo "The directory name does not exist"
fi

但是,这种方法并不是最好的方法,因为如果您检查文件 A,发现它与文件 B 相同,那么它会告诉您删除文件 B,而当它检查文件 B 时,它会告诉您删除文件 A……所以,它会先找到文件 A,再删除文件 B。在这个例子中,B 将被首先删除。一旦它尝试检查文件 B,发现 B 不再存在,它会中断循环吗?我不知道。我没有检查……

相关内容