디렉토리에서 중복 파일을 제거하는 방법?


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

참고 : 이름은 중요하지 않습니다. 나는 단지 유니크 파일을 원한다.

답변:


27

배쉬 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

이것은 재귀 적이며 모든 파일 이름을 처리합니다. 단점은 연관 배열 및 재귀 검색을 사용하려면 버전 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

@Ferroao 아마도 그들은 정확히 중복되지 않았습니다. 단 하나의 비트가 md5 해시를 벗어나면 스크립트가 이중성을 결정하는 데 사용하는 완전히 다른 것입니다. 각 파일의 해시를 보려면 echo cksm시작하는 줄 바로 뒤에 추가 할 수 있습니다 read.
SiegeX

아니요, 모든 "중복"(복사본)이 제거되었으며 버전 1 개가 남아 있습니다. 절반은 한 폴더에서, 다른 절반은 다른 폴더에서 삭제되었습니다 (100 % 사본 삭제). 내 100 %가 아닌 전체의, 초과 복사본입니다
Ferroao

@ 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

잘 작동하지만 파일 이름에 공백이 있으면 실패합니다.
Daniel Wolf

1
@DanielWolf xargs 옵션으로 시도-d '\n'
Jakob

1
또한, fdupes의 새로운 버전이 내장 된 옵션을 제외한 모든 중복 파일의 목록에서 첫 번째를 삭제 : fdupes -rdN .-r은 재귀, -d 삭제하고 -N에는 프롬프트없는 곳에
랜드

감사합니다. 두 개 이상의 중복을 감지 할 수 있고 유지하려는 딥 중 하나 (또는 ​​모두)를 선택할 수 있기 때문에이 기능이 뛰어납니다.
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

예를 들어, 우리는 약간의 dir을 가지고 있습니다 :

$> 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)을 사용하여 파일 맵과 해시를 만듭니다. 나중에 나는 그 파일과 유닉스 파이프의 마술을 사용하여 나머지를 수행합니다.

스크립트는 아무것도 삭제하지 않지만 파일을 삭제하는 명령을 인쇄합니다.

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

이것은 당신이 요구하는 것이 아니지만 체크섬이 같지 않을 때 누군가 유용하다고 생각하지만 이름은 비슷합니다 (괄호 안에 접미사 포함). 이 스크립트는 접미사가있는 파일을 ( "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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.