Як видалити повторювані файли в каталозі?


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

баш 4.х

#!/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якщо вам подобаються результати.


прекрасно, версія bash працювала для мене, але в моєму тесті з 2 подібних папок вона видалила половину дублікатів в одній папці, а половину - в іншій. чому. Я очікував би видалення всіх (дублюється) однієї папки.
Ферроао

@Ferroao Можливо, це не були точні дублікати. Якщо тільки один біт відключений хеш md5, який мій скрипт використовує для визначення подвійності, був би зовсім іншим. Ви можете додати echo cksmрядок після рядка, починаючи з, readякщо ви хочете переглянути хеш кожного файлу.
SiegeX

ні, всі "дублікати" (копії) було видалено, залишилася 1 версія, скажімо, оригінал. половину копій було видалено з однієї папки, а іншу половину з іншої папки (100% видалення копій). мої 100%
припадають

@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 - невідповідним
Rand

Дякую, цей видатний факт, що може виявити більше 2 дублікатів і дозволяє вибрати, який з дуплів ви хочете зберегти (або всі).
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

Більшість і, можливо, всі відповіді, що залишилися, надзвичайно неефективні, обчислюючи контрольну суму кожного файлу в каталозі для обробки.

Потенційно наближається до величини швидший підхід - спочатку отримати розмір кожного файлу, який є майже негайним ( lsабо stat), а потім обчислити та порівняти контрольні суми лише для файлів, що мають неповторний розмір.


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

-3

Я знайшов невелику програму, яка справді спрощує такий вид завдань: fdupes .


Будь ласка, додайте інструкції з установки та приклад використання, відповідний для цього питання.
simlev
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.