Làm thế nào để loại bỏ các tập tin trùng lặp trong một thư mục?


25

Tôi đã tải xuống rất nhiều hình ảnh trong một thư mục.
Trình tải xuống đã đổi tên các tập tin đã tồn tại.
Tôi cũng đổi tên một số tệp bằng tay.

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

Làm thế nào để loại bỏ những cái trùng lặp? Kết quả sẽ là:

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

lưu ý: tên không quan trọng. Tôi chỉ muốn tập tin uniq.

Câu trả lời:


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

Đây là cả đệ quy và xử lý bất kỳ tên tập tin. Nhược điểm là nó yêu cầu phiên bản 4.x cho khả năng sử dụng mảng kết hợp và tìm kiếm đệ quy. Xóa echonếu bạn thích kết quả.

phiên bản 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='"' *

Lưu ý rằng điều này sẽ vẫn phá vỡ trên các tệp có dấu ngoặc kép trong tên của họ. Không có cách thực sự để có được xung quanh đó với awk. Loại bỏ echonếu bạn thích kết quả.


tốt, phiên bản bash làm việc cho tôi, nhưng trong thử nghiệm của tôi, với 2 thư mục tương tự, nó đã xóa một nửa số bản sao trong một thư mục và một nửa trong thư mục kia. tại sao. tôi mong muốn xóa tất cả mọi người (trùng lặp) của một thư mục.
Ferroao

@Ferroao Có lẽ chúng không trùng lặp chính xác. Nếu chỉ một bit là băm md5 mà tập lệnh của tôi đang sử dụng để xác định sự trùng lặp sẽ hoàn toàn khác nhau. Bạn có thể thêm echo cksmngay sau dòng bắt đầu bằng readnếu bạn muốn xem băm của mỗi tệp.
Cuộc bao vây

không, tất cả "bản sao" (bản sao) đã bị xóa, còn lại 1 phiên bản, giả sử bản gốc. một nửa bản sao đã bị xóa khỏi một thư mục và nửa còn lại từ thư mục kia (xóa 100% các bản sao). 100% của tôi là cho các bản sao vượt quá, không phải là toàn bộ
Ferroao

@Ferroao tôi thấy. Trong trường hợp đó, dường như khi bash thực hiện mở rộng đường dẫn đệ quy **, nó sắp xếp danh sách theo cách hai thư mục được xen kẽ thay vì tất cả thư mục 1 sau đó tất cả thư mục 2. Tập lệnh sẽ luôn để lại 'bản gốc' đầu tiên nó nhấn khi nó lặp qua danh sách. Bạn có thể echo $filetrước readdòng để xem điều này có đúng không.
Cuộc bao vây

45

fdupes là công cụ bạn chọn. Để tìm tất cả các tệp trùng lặp (theo nội dung, không phải theo tên) trong thư mục hiện tại:

fdupes -r .

Để xác nhận xóa các tệp trùng lặp theo cách thủ công:

fdupes -r -d .

Để tự động xóa tất cả các bản sao nhưng bản đầu tiên của mỗi tệp trùng lặp ( được cảnh báo, cảnh báo này, điều này thực sự xóa các tệp, theo yêu cầu ):

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

Tôi khuyên bạn nên kiểm tra thủ công các tệp trước khi xóa:

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

Hoạt động tuyệt vời, nhưng thất bại nếu tên tệp chứa khoảng trắng.
Daniel Wolf

1
@DanielWolf thử với tùy chọn xargs-d '\n'
Jakob

1
Ngoài ra, các phiên bản mới hơn của fdupes có tùy chọn tích hợp để xóa tất cả trừ phiên bản đầu tiên trong danh sách các tệp trùng lặp: fdupes -rdN .trong đó -r là đệ quy, -d bị xóa và -N không có dấu nhắc
Rand

Cảm ơn bạn, Điều này thật nổi bật vì có thể phát hiện nhiều hơn 2 bản sao và cho phép bạn chọn một trong những phần mềm bạn muốn giữ lại (hoặc tất cả chúng).
Smeterlink


1

Là một chút lười biếng, tôi không mất nhiều thời gian để tìm thấy một trực tuyến .

Trước tiên, bạn phải tạo một tổng kiểm tra CRC của mỗi tệp, vì rõ ràng bạn chỉ muốn xóa các bản sao chính xác.

cksum  *.jpg | sort -n > filelist

Sau đó, lặp lại danh sách tập tin này, đọc trong tổng kiểm tra và cả tên tệp. Nếu hai tổng kiểm tra giống nhau, tệp sẽ bị xóa. Điều này hoạt động, vì sắp xếp là số và chỉ sắp xếp trên tổng kiểm, nhóm nào trùng lặp các tệp.

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

Rõ ràng, điều này không làm việc đệ quy.


1

Làm thế nào để kiểm tra các tập tin có nội dung độc đáo?

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

Làm thế nào chúng ta có thể nhận được danh sách các tập tin trong thư mục?

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

Chúng tôi có thể nhận bất kỳ 2 tệp nào từ danh sách đó và kiểm tra xem tên của chúng có khác nhau không và nội dung có giống nhau không.

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

Ví dụ: chúng tôi có một số thư mục:

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

Vì vậy, chỉ có 3 tập tin duy nhất.

Hãy chạy tập lệnh đó:

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

Và chúng tôi chỉ nhận được 3 tập tin.

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

1

Tôi đã viết tập lệnh nhỏ này để xóa các tập tin trùng lặp

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

Về cơ bản, nó sử dụng một tệp tạm thời ( /tmp/list.txt) để tạo bản đồ của các tệp và giá trị băm của chúng. Sau đó tôi sử dụng các tập tin đó và sự kỳ diệu của các ống Unix để làm phần còn lại.

Kịch bản sẽ không xóa bất cứ thứ gì nhưng sẽ in các lệnh để xóa các tệp.

mfilter.sh ./dir | bash

Hy vọng nó giúp


1

Phiên bản ngắn gọn hơn để loại bỏ các tệp trùng lặp (chỉ một dòng)

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

Tôi tìm thấy một cách dễ dàng hơn để thực hiện cùng một nhiệm vụ

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

0

Hầu hết và có thể tất cả các câu trả lời còn lại là không hiệu quả khủng khiếp bằng cách tính toán tổng kiểm tra của mỗi và mọi tệp trong thư mục để xử lý.

Một cách tiếp cận có khả năng tiếp cận nhanh hơn trước tiên là lấy kích thước của mỗi tệp, gần như ngay lập tức ( lshoặc stat), sau đó tính toán và so sánh tổng kiểm chỉ cho các tệp có kích thước không duy nhất.


0

Đây không phải là những gì bạn đang hỏi, nhưng tôi nghĩ ai đó có thể thấy nó hữu ích khi tổng kiểm tra không giống nhau, nhưng tên tương tự (có hậu tố trong ngoặc đơn). Kịch bản lệnh này loại bỏ các tệp có hậu tố là ("chữ số")

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

Tôi tìm thấy một chương trình nhỏ thực sự đơn giản hóa loại nhiệm vụ này: fdupes .


Vui lòng thêm hướng dẫn cài đặt và ví dụ sử dụng phù hợp cho câu hỏi.
simlev
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.