디렉토리에서 중복을 찾아서 제거


12

여러 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


질문에 관한 오류 메시지도 포함하십시오.
terdon

왜 fdupes와 같은 외부 도구를 사용할 수 없습니까? @terdon의 대답은 훌륭하지만 실제로 좋은 도구를 사용하는 것이 가능한 경우 갈 수있는 방법을 강조합니다. 전용 하드웨어 또는 서버 인 경우 fdupes와 같은 도구를 사용할 수있는 시스템에서 네트워크 등을 통해 계속 액세스 할 수 있습니다.
Joe

답변:


28

스크립트에 몇 가지 문제가 있습니다.

  • 먼저, 명령 결과 를 변수 에 할당하려면 변수를 백틱 ( `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")를 실행 하여 모든 파일을 살펴 봅니다.

  • 이다 나쁜 생각 사용하는 for발견의 출력과 루프를. while루프 또는 globbing을 사용해야합니다 .

    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'

이 작은 Perl 스크립트는 find명령 결과 (즉, md5sum 및 파일 이름)를 통해 실행됩니다 . -a에 대한 옵션 perl공백에서 분할 입력 라인과은에 저장합니다 F그래서 배열 $F[0]md5sum이 될 것 $F[1]파일 이름. md5sum은 해시에 저장되며 k스크립트는 해시가 이미 표시되었는지 확인하고 ( if $k{$F[0]}>1) 파일이 있으면 ( ) 삭제합니다 system("rm $F[1]").


작동하지만 큰 이미지 모음의 경우 속도가 매우 느리므로 보관할 파일을 선택할 수 없습니다. 보다 우아한 방식으로이를 처리하는 많은 프로그램이 있습니다.


펄 스 니펫 +1 정말 우아합니다! 전화 unlink를하는 대신 펄 자신의 것을 사용할 수도 있습니다 system.
Joseph R.

@JosephR. 감사 :). 그러나 버그가 있으면 공백이있는 파일 이름의 경우 첫 번째 공백까지의 첫 번째 문자만이 있기 때문에 실패합니다 $F[1]. 배열 슬라이스를 사용하여 수정했습니다. unlink ()에 관해서는 알고 있지만 perlisms를 최소화하고 싶었고 Perl을 모르면 시스템 호출을 이해하는 것이 더 쉽습니다.
terdon

13

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사용 가능한 모든 옵션을 참조하십시오 .

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