ディレクトリ内の重複ファイルを削除するにはどうすればよいですか?

ディレクトリ内の重複ファイルを削除するにはどうすればよいですか?

ディレクトリにたくさんの画像をダウンロードしました。
ダウンローダーは既存のファイルの名前を変更しました。
また、一部のファイルの名前を手動で変更しました。

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

注: 名前は重要ではありません。必要なのは一意のファイルだけです。

答え1

バッシュ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='"' *

ただし、ファイル名に二重引用符が含まれている場合は、この方法は機能しないことに注意してください。 を使用してこれを回避する方法はありません。結果に満足する場合は、awkを削除してください。echo

答え2

重複選択したツールです。現在のディレクトリ内のすべての重複ファイル(名前ではなく内容で)を検索するには、次のようにします。

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

答え3

お勧めしますクローン

Fclones は、Rust で書かれた最新の重複ファイル検索および削除ツールで、ほとんどの Linux ディストリビューションと macOS で利用できます。

注目すべき機能:

  • ファイルパス内のスペース、非ASCII文字、制御文字をサポートします
  • 複数のディレクトリツリーを検索できます
  • .gitignore ファイルを尊重する
  • 安全: 重複リストを手動で検査してから、何らかのアクションを実行できます。
  • 削除または保存するファイルをフィルタリング/選択するための豊富なオプションを提供します
  • とても早い

現在のディレクトリ内の重複を検索するには、次のコマンドを実行します。

fclones group . >dupes.txt

次に、dupes.txtファイルを調べて、正しい重複が見つかったかどうかを確認します (リストを好みに合わせて変更することもできます)。

最後に、次のいずれかの方法で重複ファイルを削除/リンク/移動します。

fclones remove <dupes.txt
fclones link <dupes.txt
fclones move target <dupes.txt
fclones dedupe <dupes.txt   # copy-on-write deduplication on some filesystems

例:

pkolaczk@p5520:~/Temp$ mkdir files
pkolaczk@p5520:~/Temp$ echo foo >files/foo1.txt
pkolaczk@p5520:~/Temp$ echo foo >files/foo2.txt
pkolaczk@p5520:~/Temp$ echo foo >files/foo3.txt

pkolaczk@p5520:~/Temp$ fclones group files >dupes.txt
[2022-05-13 18:48:25.608] fclones:  info: Started grouping
[2022-05-13 18:48:25.613] fclones:  info: Scanned 4 file entries
[2022-05-13 18:48:25.613] fclones:  info: Found 3 (12 B) files matching selection criteria
[2022-05-13 18:48:25.614] fclones:  info: Found 2 (8 B) candidates after grouping by size
[2022-05-13 18:48:25.614] fclones:  info: Found 2 (8 B) candidates after grouping by paths and file identifiers
[2022-05-13 18:48:25.619] fclones:  info: Found 2 (8 B) candidates after grouping by prefix
[2022-05-13 18:48:25.620] fclones:  info: Found 2 (8 B) candidates after grouping by suffix
[2022-05-13 18:48:25.620] fclones:  info: Found 2 (8 B) redundant files

pkolaczk@p5520:~/Temp$ cat dupes.txt
# Report by fclones 0.24.0
# Timestamp: 2022-05-13 18:48:25.621 +0200
# Command: fclones group files
# Base dir: /home/pkolaczk/Temp
# Total: 12 B (12 B) in 3 files in 1 groups
# Redundant: 8 B (8 B) in 2 files
# Missing: 0 B (0 B) in 0 files
6109f093b3fd5eb1060989c990d1226f, 4 B (4 B) * 3:
    /home/pkolaczk/Temp/files/foo1.txt
    /home/pkolaczk/Temp/files/foo2.txt
    /home/pkolaczk/Temp/files/foo3.txt

pkolaczk@p5520:~/Temp$ fclones remove <dupes.txt
[2022-05-13 18:48:41.002] fclones:  info: Started deduplicating
[2022-05-13 18:48:41.003] fclones:  info: Processed 2 files and reclaimed 8 B space

pkolaczk@p5520:~/Temp$ ls files
foo1.txt

答え4

固有のコンテンツを持つファイルをテストするにはどうすればよいですか?

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)

関連情報