Bagaimana cara menghapus file yang digandakan dalam direktori?


25

Saya mengunduh banyak gambar dalam direktori.
Downloader mengganti nama file yang sudah ada.
Saya juga mengganti nama beberapa file secara manual.

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

Bagaimana cara menghapus yang digandakan? Hasilnya harus:

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

catatan: nama tidak masalah. Saya hanya ingin file uniq.

Jawaban:


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

Ini bersifat rekursif dan menangani nama file apa pun. Kelemahannya adalah ia membutuhkan versi 4.x untuk kemampuan menggunakan array asosiatif dan pencarian rekursif. Hapus echojika Anda suka hasilnya.

versi melongo

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

Perhatikan bahwa ini masih akan memecah pada file yang memiliki tanda kutip ganda dalam namanya. Tidak ada cara nyata untuk menyiasatinya awk. Hapus echojika Anda suka hasilnya.


baiklah, versi bash bekerja untuk saya, tetapi dalam pengujian saya, dengan 2 folder yang sama, itu menghapus setengah dari duplikat di satu folder, dan setengah di yang lain. Mengapa. saya harapkan penghapusan semua orang (digandakan) dari satu folder.
Ferroao

@ Ferroao Mungkin mereka bukan duplikat yang tepat. Jika hanya satu bit mati hash md5 yang digunakan skrip saya untuk menentukan duplikat akan sangat berbeda. Anda bisa menambahkan echo cksmtepat setelah baris dimulai dengan readjika Anda ingin melihat hash setiap file.
SiegeX

tidak, semua "duplikat" (salinan) telah dihapus, tersisa 1 versi, katakanlah yang asli. setengah salinan dihapus dari satu folder, dan setengah lainnya dari folder lainnya (penghapusan 100% salinan). 100% saya adalah untuk salinan yang berlebihan, bukan dari totalitas
Ferroao

@Ferroao saya mengerti. Dalam hal ini tampaknya ketika bash melakukan ekspansi jalur rekursif melalui **, itu memerintahkan daftar sedemikian rupa sehingga dua folder disisipkan daripada semua folder 1 lalu semua folder 2. Skrip akan selalu meninggalkan 'asli' pertama itu hits saat iterates melalui daftar. Anda dapat echo $filesebelum readbaris untuk melihat apakah ini benar.
SiegeX

45

fdupes adalah alat pilihan Anda. Untuk menemukan semua file duplikat (berdasarkan konten, bukan berdasarkan nama) di direktori saat ini:

fdupes -r .

Untuk secara manual mengkonfirmasi penghapusan file duplikat:

fdupes -r -d .

Untuk secara otomatis menghapus semua salinan tetapi yang pertama dari setiap file duplikat ( diperingatkan, peringatan ini, ini sebenarnya menghapus file, seperti yang diminta ):

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

Saya akan merekomendasikan untuk memeriksa file secara manual sebelum dihapus:

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

Berfungsi bagus, tetapi gagal jika nama file berisi spasi.
Daniel Wolf

1
@DanielWolf coba dengan opsi xargs-d '\n'
Jakob

1
Juga, versi yang lebih baru dari fdupes memiliki opsi bawaan untuk menghapus semua kecuali yang pertama dalam daftar file duplikat: di fdupes -rdN .mana -r adalah rekursif, -d adalah menghapus dan -N tidak ada prompt
Rand

Terima kasih, Ini luar biasa karena dapat mendeteksi lebih dari 2 duplikat dan memungkinkan Anda untuk memilih salah satu dari dups yang ingin Anda pertahankan (atau semuanya).
Smeterlink


1

Menjadi sedikit malas, tidak butuh waktu lama bagi saya untuk menemukannya secara online .

Pertama-tama Anda harus membuat checksum CRC dari setiap file, karena Anda jelas hanya ingin menghapus duplikat yang tepat.

cksum  *.jpg | sort -n > filelist

Kemudian, ulangi daftar file ini, baca di checksum dan juga nama file. Jika dua checksum sama, file akan dihapus. Ini berfungsi, karena pengurutannya numerik, dan hanya mengurutkan pada checksum, yang mengelompokkan file duplikat.

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

Jelas, ini tidak bekerja secara rekursif.


1

Bagaimana cara menguji file yang memiliki konten unik?

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

Bagaimana kita bisa mendapatkan daftar file dalam direktori?

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

Kami dapat memperoleh 2 file dari daftar itu dan memeriksa apakah namanya berbeda dan isinya sama.

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

Sebagai contoh, kami memiliki beberapa dir:

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

Jadi hanya ada 3 file unik.

Mari kita jalankan skrip itu:

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

Dan kami hanya mendapatkan 3 file.

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

1

Saya menulis skrip kecil ini untuk menghapus file yang digandakan

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

Pada dasarnya ia menggunakan file sementara ( /tmp/list.txt) untuk membuat peta file dan hash-nya. Kemudian saya menggunakan file itu dan keajaiban pipa Unix untuk melakukan sisanya.

Script tidak akan menghapus apa pun tetapi akan mencetak perintah untuk menghapus file.

mfilter.sh ./dir | bash

Semoga ini bisa membantu


1

Versi lebih ringkas untuk menghapus file yang digandakan (hanya satu baris)

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

Saya menemukan cara yang lebih mudah untuk melakukan tugas yang sama

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

0

Sebagian besar dan mungkin semua jawaban yang tersisa sangat tidak efisien dengan menghitung checksum dari setiap file dalam direktori untuk diproses.

Pendekatan urutan besarnya yang berpotensi lebih cepat adalah pertama-tama mendapatkan ukuran setiap file, yang hampir segera ( lsatau stat), dan kemudian menghitung dan membandingkan checksum hanya untuk file yang memiliki ukuran tidak unik.


0

Ini bukan yang Anda tanyakan, tetapi saya pikir seseorang mungkin merasa berguna ketika checksum tidak sama, tetapi namanya serupa (dengan akhiran dalam tanda kurung). Script ini menghapus file dengan akhiran sebagai ("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

Saya menemukan sebuah program kecil yang benar-benar menyederhanakan tugas semacam ini: fdupes .


Silakan tambahkan instruksi instalasi dan contoh penggunaan yang sesuai untuk pertanyaan.
simlev
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.