如何删除目录中的重复文件?


25

我在目录中下载了很多图像。
下载程序已重命名已存在的文件。
我还手动重命名了一些文件。

a.jpg
b.jpg
b(2).jpg
hello.jpg      <-- manually renamed `b(3).jpg`
c.jpg
c(2).jpg
world.jpg      <-- manually renamed `d.jpg`
d(2).jpg
d(3).jpg

如何删除重复的?结果应为:

a.jpg
b.jpg
c.jpg
world.jpg

注意:名称无关紧要。我只想要uniq文件。

Answers:


27

bash 4.x

#!/bin/bash
declare -A arr
shopt -s globstar

for file in **; do
  [[ -f "$file" ]] || continue

  read cksm _ < <(md5sum "$file")
  if ((arr[$cksm]++)); then 
    echo "rm $file"
  fi
done

这既是递归的,也可以处理任何文件名。缺点是需要使用4.x版才能使用关联数组和递归搜索。echo如果您喜欢结果,请删除。

gawk版本

gawk '
  {
    cmd="md5sum " q FILENAME q
    cmd | getline cksm
    close(cmd)
    sub(/ .*$/,"",cksm)
    if(a[cksm]++){
      cmd="echo rm " q FILENAME q
      system(cmd)
      close(cmd)
    }
    nextfile
  }' q='"' *

请注意,这仍然会破坏名称中带有双引号的文件。没有真正的方法可以解决这个问题awkecho如果您喜欢结果,请删除。


很好,bash版本对我有用,但是在我的测试中,有2个类似的文件夹,它删除了一个文件夹中一半的重复项,而另一个文件夹中的一半删除了。为什么。我希望删除一个文件夹的每个人(重复)。
Ferroao

@Ferroao也许它们不是精确的重复。如果我的脚本用来确定重复性的md5哈希值仅差一点,那将是完全不同的。如果要查看每个文件的哈希值,可以echo cksm在开头的行后添加一个read
SiegeX '17

不,所有“重复项”(副本)均已删除,剩下1个版本,例如原始版本。一半的副本从一个文件夹中删除,另一半从另一文件夹中删除(100%删除副本)。我的100%用于多余的副本,而不是全部副本
Ferroao

@Ferroao我明白了。在那种情况下,当bash通过进行递归路径扩展时**,它会以以下方式对列表进行排序:两个文件夹是交错的,而不是文件夹1的全部然后是文件夹2的全部。脚本将始终保留第一个“原始”文件夹遍历列表时命中。您可以echo $file在该read行之前查看是否为真。
SiegeX

45

fdupes是您选择的工具。要在当前目录中查找所有重复文件(按内容而不是名称):

fdupes -r .

要手动确认删除重复文件:

fdupes -r -d .

要自动删除除每个重复文件的第一个文件外的所有副本(请注意,此警告实际上会根据请求删除文件):

fdupes -r -f . | grep -v '^$' | xargs rm -v

建议删除之前手动检查文件:

fdupes -rf . | grep -v '^$' > files
... # check files
xargs -a files rm -v

效果很好,但如果文件名包含空格,则失败。
丹尼尔·沃尔夫

1
@DanielWolf尝试使用xargs选项-d '\n'
Jakob

1
同样,较新版本的fdupes具有内置选项,可删除重复文件列表中除第一个文件外的所有文件:fdupes -rdN .其中-r是递归的,-d是删除,-N是无提示
兰德

谢谢,这很出色,因为它可以检测到两个以上的重复项,并允许您选择要保留的其中一个(或全部)重复项。
Smeterlink


1

有点懒,很快我就在网上找到了一个

首先,您必须为每个文件创建一个CRC校验和,因为您显然只想删除精确的重复项。

cksum  *.jpg | sort -n > filelist

然后,遍历此文件列表,读取校验和以及文件名。如果两个校验和相同,则文件将被删除。这是可行的,因为排序是数字形式的,并且仅对校验和进行排序,校验和将重复文件分组。

old=""
while read sum lines filename
do
      if [[ "$sum" != "$old" ]] ; then
            old="$sum"
            continue
      fi
      rm -f "$filename"
done < filelist

显然,这不是递归的。


1

如何测试具有唯一内容的文件?

if diff "$file1" "$file2" > /dev/null; then
    ...

我们如何获取目录中的文件列表?

files="$( find ${files_dir} -type f )"

我们可以从该列表中获取任意2个文件,并检查它们的名称是否不同以及内容是否相同。

#!/bin/bash
# removeDuplicates.sh

files_dir=$1
if [[ -z "$files_dir" ]]; then
    echo "Error: files dir is undefined"
fi

files="$( find ${files_dir} -type f )"
for file1 in $files; do
    for file2 in $files; do
        # echo "checking $file1 and $file2"
        if [[ "$file1" != "$file2" && -e "$file1" && -e "$file2" ]]; then
            if diff "$file1" "$file2" > /dev/null; then
                echo "$file1 and $file2 are duplicates"
                rm -v "$file2"
            fi
        fi
    done
done

例如,我们有一些目录:

$> ls .tmp -1
all(2).txt
all.txt
file
text
text(2)

因此,只有3个唯一文件。

让我们运行该脚本:

$> ./removeDuplicates.sh .tmp/
.tmp/text(2) and .tmp/text are duplicates
removed `.tmp/text'
.tmp/all.txt and .tmp/all(2).txt are duplicates
removed `.tmp/all(2).txt'

而且我们只剩下3个文件。

$> ls .tmp/ -1
all.txt
file
text(2)

1

我写了这个小脚本来删除重复的文件

https://gist.github.com/crodas/d16a16c2474602ad725b

基本上,它使用一个临时文件(/tmp/list.txt)创建文件及其哈希表。后来,我使用该文件和Unix管道的魔力来完成其余的工作。

该脚本不会删除任何内容,但是会打印命令以删除文件。

mfilter.sh ./dir | bash

希望能帮助到你


1

删除重复文件的更简洁版本(仅一行)

young@ubuntu-16:~/test$ md5sum `find ./ -type f` | sort -k1 | uniq -w32 -d | xargs rm -fv

find_same_size.sh

#!/usr/bin/env bash
#set -x
#This is small script can find same size of files.
find_same_size(){

if [[ -z $1 || ! -d $1 ]]
then
echo "Usage $0 directory_name" ;
 exit $?
else
dir_name=$1;
echo "current directory is $1"



for i in $(find $dir_name -type f); do
   ls -fl $i
done | awk '{f=""
        if(NF>9)for(i=9;i<=NF;i++)f=f?f" "$i:$i; else f=$9;
        if(a[$5]){ a[$5]=a[$5]"\n"f; b[$5]++;} else a[$5]=f} END{for(x     in b)print a[x] }' | xargs stat -c "%s  %n" #For just list files
 fi
   }

find_same_size $1


young@ubuntu-16:~/test$ bash find_same_size.sh tttt/ | awk '{ if($1 !~   /^([[:alpha:]])+/) print $2}' | xargs md5sum | uniq -w32 -d | xargs rm -vf

0

我发现了执行相同任务的简便方法

for i in `md5sum * | sort -k1 | uniq -w32 -d|awk '{print $2}'`; do
rm -rf $i
done

0

通过计算要处理的目录中每个文件的校验和,大多数(甚至可能是所有)其余答案的效率都非常低。

一种可能快几个数量级的方法是,首先获取几乎是立即数(lsstat)的每个文件的大小,然后仅对具有非唯一大小的文件计算和比较校验和。


0

这不是您要的内容,但我认为当校验和不相同但名称相似(括号中带有后缀)时,有人会发现它很有用。该脚本删除后缀为(“数字”)的文件

#! /bin/bash
# Warning: globstar excludes hidden directories.
# Turn on recursive globbing (in this script) or exit if the option is not supported:
shopt -s globstar || exit
for f in **
do
extension="${f##*.}"
#get only files with parentheses suffix
FILEWITHPAR=$( echo "${f%.*}".$extension | grep -o -P "(.*\([0-9]\)\..*)")
# print file to be possibly deleted
if [ -z "$FILEWITHPAR" ] ;then
:
else
echo "$FILEWITHPAR ident"
# identify if a similar file without suffix exists
FILENOPAR=$(echo $FILEWITHPAR | sed -e 's/^\(.*\)([0-9])\(.*\).*/\1\2/')
echo "$FILENOPAR exists?"
if [ -f "$FILENOPAR" ]; then
#delete file with suffix in parentheses
echo ""$FILEWITHPAR" to be deleted"
rm -Rf "$FILEWITHPAR"
else
echo "no"
fi
fi
done

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.