ディレクトリ内の重複ファイルを削除する方法は?


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ファイルが欲しいだけです。

回答:


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つの類似したフォルダーで、1つのフォルダーの半分と、もう1つのフォルダーの半分を削除しました。なぜ。1つのフォルダーの(重複した)全員の削除を期待します。
Ferroao

@Ferroaoおそらくそれらは正確な複製ではありませんでした。重複を判断するためにスクリプトが使用しているmd5ハッシュから1ビットだけが外れている場合、まったく異なります。各ファイルのハッシュを表示echo cksmするread場合は、次の行で始まる行を追加できます。
SiegeX

いいえ、すべての「複製」(コピー)は削除され、残りの1バージョンは元のバージョンとしましょう。半分のコピーが1つのフォルダーから削除され、残りの半分が他のフォルダーから削除されました(コピーの100%削除)。私の100%は過剰ではなく、全体のコピーのためである
Ferroao

@Ferroaoなるほど。その場合、bashがを介して再帰的なパス拡張を行うと**、フォルダー1のすべてではなく、フォルダー2のすべてではなく、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は無プロンプトではありません
ランド

ありがとう、これは2つ以上の重複を検出することができ、保存したい重複の1つ(またはそれらすべて)を選択できるため、優れています。
Smeterlink


1

少し怠けていたので、オンラインで見つけるのに時間がかかりませんでした。

明らかに正確な重複のみを削除したいので、最初に各ファイルのCRCチェックサムを作成する必要があります。

cksum  *.jpg | sort -n > filelist

次に、このファイルリストを反復処理し、チェックサムとファイル名を読み取ります。2つのチェックサムが同じ場合、ファイルは削除されます。ソートは数値であり、重複ファイルをグループ化するチェックサムでのみソートされるため、これは機能します。

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

重複ファイルの削除のより簡潔なバージョン(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

処理するディレクトリ内のすべてのファイルのチェックサムを計算することにより、残りの回答のほとんどおよびおそらくすべてが非常に非効率的です。

潜在的に桁違いに高速なアプローチは、最初に各ファイルのサイズを取得することです。これは、ほぼ即時(lsまたはstat)であり、次に、一意でないサイズのファイルについてのみチェックサムを計算および比較します。


0

これはあなたが尋ねていることではありませんが、チェックサムが同じではないが、名前が似ている場合(括弧内に接尾辞を付けた場合)誰かが役に立つと思うかもしれません。このスクリプトは、サフィックスが( "digit")のファイルを削除します

#! /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

-3

この種のタスクを本当に単純化する小さなプログラムfdupesを見つけました。


質問に適したインストール手順と使用例を追加してください。
simlev
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.