Wie entferne ich doppelte Dateien in einem Verzeichnis?

25

Ich habe viele Bilder in ein Verzeichnis heruntergeladen.
Der Downloader hat bereits vorhandene Dateien umbenannt.
Ich habe auch einige der Dateien manuell umbenannt.

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

Wie entferne ich doppelte? Das Ergebnis sollte sein:

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

Hinweis: Der Name spielt keine Rolle. Ich möchte nur Uniq-Dateien.

kev
quelle

Antworten:

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

Dies ist sowohl rekursiv als auch behandelt jeden Dateinamen. Der Nachteil ist, dass Version 4.x für die Verwendung von assoziativen Arrays und für die rekursive Suche erforderlich ist. Entfernen Sie die, echowenn Sie die Ergebnisse mögen.

gawk version

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='"' *

Beachten Sie, dass dies bei Dateien, deren Name doppelte Anführungszeichen enthält, immer noch nicht funktioniert. Keine wirkliche Möglichkeit, damit umzugehen awk. Entfernen Sie die, echowenn Sie die Ergebnisse mögen.

SiegeX
quelle
In Ordnung, die Bash-Version hat bei mir funktioniert, aber in meinem Test hat sie bei 2 ähnlichen Ordnern die Hälfte der Duplikate in einem Ordner und die Hälfte in dem anderen Ordner gelöscht. Warum. Ich würde erwarten, dass alle (duplizierten) Ordner eines Ordners gelöscht werden.
Ferroao
@Ferroao Vielleicht waren sie keine exakten Duplikate. Wenn nur ein Bit ausfällt, ist der MD5-Hash, den mein Skript verwendet, um die Duplizität zu bestimmen, völlig anders. Sie können echo cksmnach der Zeile, die mit beginnt , eine hinzufügen, readwenn Sie den Hash jeder Datei sehen möchten.
SiegeX
nein, alle "Duplikate" (Kopien) wurden entfernt, verbleibende 1 Version, sagen wir das Original. Die Hälfte der Kopien wurde aus einem Ordner und die andere Hälfte aus dem anderen Ordner gelöscht (100% ige Löschung der Kopien). meine 100% ist für Kopien im Übermaß, nicht von der Gesamtheit
Ferroao
@Ferroao Ich verstehe. In diesem Fall sieht es so aus, als ob bash bei der rekursiven Pfaderweiterung über **die Option die Liste so anordnet, dass die beiden Ordner verschachtelt sind und nicht der gesamte Ordner 1 und der gesamte Ordner 2. Das Skript belässt immer das erste "Original". es trifft, während es die Liste durchläuft. Sie können echo $filevor der readZeile sehen, ob dies zutrifft.
SiegeX
45

fdupes ist das Werkzeug Ihrer Wahl. So finden Sie alle doppelten Dateien (nach Inhalt, nicht nach Name) im aktuellen Verzeichnis:

fdupes -r .

So bestätigen Sie das Löschen duplizierter Dateien manuell:

fdupes -r -d .

So löschen Sie automatisch alle Kopien außer der ersten von jeder duplizierten Datei ( seien Sie gewarnt, diese Warnung löscht tatsächlich Dateien, wie angefordert ):

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

Ich würde empfehlen, die Dateien vor dem Löschen manuell zu überprüfen:

fdupes -rf . | grep -v '^$' > files
... # check files
xargs -a files rm -v
Jakob
quelle
Funktioniert hervorragend, schlägt jedoch fehl, wenn Dateinamen Leerzeichen enthalten.
Daniel Wolf
1
@ Danielwolf versuchen mit xargs Option-d '\n'
Jakob
1
Neuere Versionen von fdupes haben außerdem die eingebaute Option, alle bis auf den ersten Eintrag in einer Liste doppelter Dateien zu löschen: fdupes -rdN .Dabei ist -r rekursiv, -d ist delete und -N ist keine Eingabeaufforderung
Rand
Vielen Dank. Dies ist hervorragend, da mehr als zwei Duplikate erkannt werden und Sie auswählen können, welches der Dups Sie beibehalten möchten (oder alle).
Smeterlink
1

Da ich ein bisschen faul bin, habe ich nicht lange gebraucht, um eine online zu finden .

Sie müssen zunächst eine CRC-Prüfsumme für jede Datei erstellen, da Sie natürlich nur exakte Duplikate entfernen möchten.

cksum  *.jpg | sort -n > filelist

Durchlaufen Sie dann diese Dateiliste und lesen Sie die Prüfsumme sowie den Dateinamen ein. Wenn zwei Prüfsummen identisch sind, wird die Datei entfernt. Dies funktioniert, da die Sortierung numerisch ist und nur nach den Prüfsummen sortiert wird, die doppelte Dateien gruppieren.

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

Dies funktioniert natürlich nicht rekursiv.

slhck
quelle
1

Wie teste ich Dateien mit einzigartigem Inhalt?

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

Wie können wir eine Liste der Dateien im Verzeichnis bekommen?

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

Wir können 2 beliebige Dateien aus dieser Liste abrufen und prüfen, ob ihre Namen unterschiedlich sind und der Inhalt identisch ist.

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

Zum Beispiel haben wir ein Verzeichnis:

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

Es gibt also nur 3 eindeutige Dateien.

Lass uns das Skript ausführen:

$> ./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'

Und wir bekommen nur 3 Dateien hinterlassen.

$> ls .tmp/ -1
all.txt
file
text(2)
ДМИТРИЙ МАЛИКОВ
quelle
1

Ich habe dieses winzige Skript geschrieben, um doppelte Dateien zu löschen

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

Grundsätzlich wird eine temporäre Datei ( /tmp/list.txt) verwendet, um eine Karte der Dateien und ihrer Hashes zu erstellen. Später benutze ich diese Dateien und die Magie von Unix-Pipes, um den Rest zu erledigen.

Das Skript löscht nichts, druckt jedoch die Befehle zum Löschen von Dateien.

mfilter.sh ./dir | bash

Ich hoffe es hilft

Crodas
quelle
1

Präzisere Version zum Entfernen doppelter Dateien (nur eine Zeile)

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
oh niceguy
quelle
0

Ich habe einen einfacheren Weg gefunden, um die gleiche Aufgabe auszuführen

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

Die meisten und möglicherweise alle verbleibenden Antworten sind schrecklich ineffizient, wenn die Prüfsumme jeder einzelnen Datei im zu verarbeitenden Verzeichnis berechnet wird.

Ein potenziell um Größenordnungen schnellerer Ansatz besteht darin, zunächst die Größe jeder Datei zu ermitteln, die fast unmittelbar ( lsoder stat) vorliegt, und dann die Prüfsummen nur für Dateien mit einer nicht eindeutigen Größe zu berechnen und zu vergleichen.

jlliagre
quelle
0

Dies ist nicht das, was Sie fragen, aber ich denke, jemand könnte es nützlich finden, wenn die Prüfsummen nicht gleich sind, aber der Name ähnlich ist (mit Suffix in Klammern). Dieses Skript entfernt die Dateien mit dem Suffix ("Ziffer")

#! /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
Ferroao
quelle
-3

Ich habe ein kleines Programm gefunden, das diese Art von Aufgaben wirklich vereinfacht: fdupes .

Ricky Neff
quelle
Bitte fügen Sie der Frage eine Installationsanleitung und ein Verwendungsbeispiel hinzu.
Simlev