Comment supprimer les fichiers dupliqués dans un répertoire?


25

J'ai téléchargé beaucoup d'images dans un répertoire.
Downloader a renommé des fichiers qui existent déjà.
J'ai également renommé certains fichiers manuellement.

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

Comment supprimer les doublons? Le résultat devrait être:

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

note: le nom n'a pas d'importance. Je veux juste des fichiers uniq.

Réponses:


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

C'est à la fois récursif et gère n'importe quel nom de fichier. L'inconvénient est qu'il nécessite la version 4.x pour pouvoir utiliser des tableaux associatifs et une recherche récursive. Retirez le echosi vous aimez les résultats.

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

Notez que cela se cassera toujours sur les fichiers qui ont des guillemets doubles dans leur nom. Aucun moyen réel de contourner cela awk. Retirez le echosi vous aimez les résultats.


très bien, la version bash a fonctionné pour moi, mais dans mon test, avec 2 dossiers similaires, elle a supprimé la moitié des doublons dans un dossier et la moitié dans l'autre. Pourquoi. je m'attendrais à la suppression de tout le monde (en double) d'un dossier.
Ferroao

@Ferroao Peut-être n'étaient-ils pas des doublons exacts. Si un seul bit est désactivé, le hachage md5 que mon script utilise pour déterminer la duplicité serait complètement différent. Vous pouvez ajouter un echo cksmjuste après la ligne commençant par readsi vous voulez voir le hachage de chaque fichier.
SiegeX

non, tous les "doublons" (copies) ont été supprimés, il reste 1 version, disons l'original. la moitié des copies a été supprimée d'un dossier et l'autre moitié de l'autre dossier (suppression à 100% des copies). mon 100% est pour les copies en excès, pas de la totalité
Ferroao

@Ferroao je vois. Dans ce cas, il semble que lorsque bash fait son expansion de chemin récursif via **, il ordonne la liste de manière à ce que les deux dossiers soient entrelacés plutôt que tout le dossier 1 puis tout le dossier 2. Le script laissera toujours le premier `` original '' il frappe pendant qu'il parcourt la liste. Vous pouvez echo $fileavant la readligne pour voir si cela est vrai.
SiegeX

45

fdupes est l'outil de votre choix. Pour rechercher tous les fichiers en double (par contenu, pas par nom) dans le répertoire actuel:

fdupes -r .

Pour confirmer manuellement la suppression des fichiers dupliqués:

fdupes -r -d .

Pour supprimer automatiquement toutes les copies sauf la première de chaque fichier dupliqué ( soyez averti, cet avertissement, cela supprime en fait les fichiers, comme demandé ):

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

Je recommande de vérifier manuellement les fichiers avant la suppression:

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

Fonctionne très bien, mais échoue si les noms de fichiers contiennent des espaces.
Daniel Wolf

1
@DanielWolf essaie avec l'option xargs-d '\n'
Jakob

1
De plus, les versions les plus récentes de fdupes ont l'option intégrée pour supprimer tout sauf le premier d'une liste de fichiers en double: fdupes -rdN .où -r est récursif, -d est supprimer et -N est sans invite
Rand

Merci, c'est exceptionnel car il peut détecter plus de 2 doublons et vous permet de sélectionner lequel des doublons vous souhaitez conserver (ou tous).
Smeterlink


1

Étant un peu paresseux, il ne m'a pas fallu longtemps pour en trouver un en ligne .

Vous devez d'abord créer une somme de contrôle CRC de chaque fichier, car vous ne voulez évidemment supprimer que les doublons exacts.

cksum  *.jpg | sort -n > filelist

Ensuite, parcourez cette liste de fichiers, en lisant la somme de contrôle et également le nom de fichier. Si deux sommes de contrôle sont identiques, le fichier sera supprimé. Cela fonctionne, car le tri est numérique et ne trie que les sommes de contrôle, qui regroupent les fichiers en double.

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

De toute évidence, cela ne fonctionne pas récursivement.


1

Comment tester des fichiers ayant un contenu unique?

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

Comment obtenir la liste des fichiers dans le répertoire?

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

Nous pouvons obtenir 2 fichiers de cette liste et vérifier si leurs noms sont différents et leur contenu identique.

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

Par exemple, nous avons un dir:

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

Il n'y a donc que 3 fichiers uniques.

Permet d'exécuter ce script:

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

Et nous obtenons seulement 3 fichiers sortis.

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

1

J'ai écrit ce petit script pour supprimer les fichiers dupliqués

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

Fondamentalement, il utilise un fichier temporaire ( /tmp/list.txt) pour créer une carte des fichiers et leurs hachages. Plus tard, j'utilise ces fichiers et la magie des tuyaux Unix pour faire le reste.

Le script ne supprimera rien mais imprimera les commandes pour supprimer les fichiers.

mfilter.sh ./dir | bash

J'espère que ça aide


1

Version plus concise de la suppression des fichiers en double (une seule ligne)

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

J'ai trouvé un moyen plus simple d'effectuer la même tâche

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

0

La plupart et peut-être toutes les réponses restantes sont terriblement inefficaces en calculant la somme de contrôle de chaque fichier du répertoire à traiter.

Une approche potentiellement plus rapide de l'ordre de grandeur consiste à obtenir d'abord la taille de chaque fichier, qui est presque immédiate ( lsou stat), puis à calculer et à comparer les sommes de contrôle uniquement pour les fichiers ayant une taille non unique.


0

Ce n'est pas ce que vous demandez, mais je pense que quelqu'un pourrait le trouver utile lorsque les sommes de contrôle ne sont pas les mêmes, mais le nom est similaire (avec le suffixe entre parenthèses). Ce script supprime les fichiers avec des suffixes comme ("chiffre")

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

J'ai trouvé un petit programme qui simplifie vraiment ce genre de tâches: fdupes .


Veuillez ajouter des instructions d'installation et un exemple d'utilisation appropriés à la question.
simlev
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.