디렉토리에서 중복 항목 찾기 및 제거

디렉토리에서 중복 항목 찾기 및 제거

여러 개의 img 파일이 포함된 디렉토리가 있는데 그 중 일부는 동일하지만 모두 이름이 다릅니다. 중복 항목을 제거해야 하는데 외부 도구가 없고 bash스크립트만 있습니다. 저는 리눅스 초보자입니다. 중첩된 for 루프를 사용하여 합계를 비교하고 결과에 따라 삭제해 보았지만 md5구문에 문제가 있어서 작동하지 않습니다. 도움이 필요하세요?

제가 시도해본 결과는...

for i in directory_path; do
    sum1='find $i -type f -iname "*.jpg" -exec md5sum '{}' \;'
    for j in directory_path; do
        sum2='find $j -type f -iname "*.jpg" -exec md5sum '{}' \;'
        if test $sum1=$sum2 ; then rm $j ; fi
    done
done

나는 얻다:test: too many arguments

답변1

스크립트에 많은 문제가 있습니다.

  • 먼저 할당을 위해서는결과명령을 변수에 전달할 때는 역따옴표( )로 묶어야 합니다. `command`아니면 작은 따옴표()로 $(command)묶어야 합니다. 'command'그러면 명령의 결과를 변수에 할당하는 대신 명령 자체가 끈. 따라서 귀하의 test실제 내용은 다음과 같습니다.

    $ echo "test $sum1=$sum2"
    test find $i -type f -iname "*.jpg" -exec md5sum {} \;=find $j -type f -iname "*.jpg" -exec md5sum {} \;
    
  • 다음 문제는 명령이 md5sum단순한 해시 이상의 것을 반환한다는 것입니다.

    $ md5sum /etc/fstab
    46f065563c9e88143fa6fb4d3e42a252  /etc/fstab
    

    첫 번째 필드만 비교하려고 하므로 md5sum첫 번째 필드만 인쇄하는 명령을 전달하여 출력을 구문 분석해야 합니다.

    find $i -type f -iname "*.png" -exec md5sum '{}' \; | cut -f 1 -d ' '
    

    또는

    find $i -type f -iname "*.png" -exec md5sum '{}' \; | awk '{print $1}' 
    
  • 또한 이 find명령은 하나가 아닌 많은 일치 항목을 반환하며 각 일치 항목은 두 번째 일치 항목에서 반복됩니다 find. 이는 어느 시점에서 동일한 파일을 자신과 비교하면 md5sum이 동일하고 결국 삭제된다는 것을 의미합니다.모두a.jpg귀하의 파일( 및 가 포함된 테스트 디렉토리에서 이것을 실행했습니다 b.jpg):

    for i in $(find . -iname "*.jpg"); do
      for j in $(find . -iname "*.jpg"); do
         echo "i is: $i and j is: $j"
      done
    done   
    i is: ./a.jpg and j is: ./a.jpg   ## BAD, will delete a.jpg
    i is: ./a.jpg and j is: ./b.jpg
    i is: ./b.jpg and j is: ./a.jpg
    i is: ./b.jpg and j is: ./b.jpg   ## BAD will delete b.jpg
    
  • for i in directory_path디렉터리 배열을 전달하지 않으면 실행하고 싶지 않습니다. 이러한 파일이 모두 동일한 디렉토리에 있으면 for i in $(find directory_path -iname "*.jpg")를 실행하여 모두 반복해야 합니다.

  • 이것은나쁜 생각forfind 출력에 루프를 사용하십시오. while루프를 사용하거나와일드카드:

    find . -iname "*.jpg" | while read i; do [...] ; done
    

    또는 모든 파일이 동일한 디렉터리에 있는 경우:

    for i in *jpg; do [...]; done
    

    쉘과 설정한 옵션에 따라 하위 디렉토리의 파일에 와일드카드를 사용할 수도 있지만 여기서는 이에 대해 논의하지 않습니다.

  • 마지막으로 변수도 인용해야 합니다. 그렇지 않으면 공백이 포함된 디렉터리 경로로 인해 스크립트가 중단됩니다.

파일 이름에는 공백, 줄 바꿈, 백슬래시 및 기타 이상한 문자가 포함될 수 있으며 루프에서 이러한 문자를 올바르게 처리하려면 while더 많은 옵션을 추가해야 합니다. 당신이 쓰고 싶은 내용은 다음과 같습니다.

find dir_path -type f -iname "*.jpg" -print0 | while IFS= read -r -d '' i; do
  find dir_path -type f -iname "*.jpg" -print0 | while IFS= read -r -d '' j; do
    if [ "$i" != "$j" ]
    then
      sum1=$(md5sum "$i" | cut -f 1 -d ' ' )
      sum2=$(md5sum "$j" | cut -f 1 -d ' ' )
      [ "$sum1" = "$sum2" ] && rm "$j"
    fi
  done
done

더 쉬운 방법은 다음과 같습니다.

find directory_path -name "*.jpg" -exec md5sum '{}' + | 
 perl -ane '$k{$F[0]}++; system("rm $F[1]") if $k{$F[0]}>1'

파일 이름의 공백을 처리할 수 있는 더 나은 버전:

find directory_path -name "*.jpg" -exec md5sum '{}' + | 
 perl -ane '$k{$F[0]}++; system("rm \"@F[1 .. $#F]\"") if $k{$F[0]}>1'

find이 작은 Perl 스크립트는 명령의 결과(즉, md5sum 및 파일 이름)를 실행합니다 . 옵션은 md5sum 및 파일 이름과 마찬가지로 입력 줄을 공백으로 분할하여 -a배열에 저장합니다. md5sum이 해시에 저장되면 스크립트는 해시가 표시되었는지 확인하고( ), 그렇다면 파일을 삭제합니다( ).perlF$F[0]$F[1]kif $k{$F[0]}>1system("rm $F[1]")


이것이 작동하는 동안 대규모 이미지 컬렉션의 경우 속도가 매우 느려지고 보관할 파일을 선택할 수 없습니다. 다음을 포함하여 보다 우아한 방식으로 이 문제를 처리하는 많은 프로그램이 있습니다.

답변2

fdupes전체 프로세스를 단순화하고 사용자에게 중복 항목을 제거하라는 메시지를 표시 하는 멋진 프로그램이 있습니다 . 확인해 볼 가치가 있다고 생각합니다.

$ fdupes --delete DIRECTORY_WITH_DUPLICATES
[1] DIRECTORY_WITH_DUPLICATES/package-0.1-linux.tar.gz        
[2] DIRECTORY_WITH_DUPLICATES/package-0.1-linux.tar.gz.1

Set 1 of 1, preserve files [1 - 2, all]: 1

   [+] DIRECTORY_WITH_DUPLICATES/package-0.1-linux.tar.gz
   [-] DIRECTORY_WITH_DUPLICATES/package-0.1-linux.tar.gz.1

기본적으로 어떤 파일을 선택할지 묻는 메시지가 표시됩니다.유지하다, 나는 들어갔다1, 두 번째 항목을 삭제했습니다.

다른 흥미로운 옵션은 다음과 같습니다.

-r --recurse
    for every directory given follow subdirectories encountered within

-N --noprompt
    when used together with --delete, preserve the first file in each set of duplicates and delete the others without prompting the user

귀하의 예에서 다음과 같이 실행할 수 있습니다.

fdupes --recurse --delete --noprompt DIRECTORY_WITH_DUPLICATES

사용 가능한 모든 옵션 은 리소스를 참조하세요 man fdupes.

관련 정보