중복 파일 찾기 및 심볼릭 링크로 교체


16

주어진 디렉토리에서 중복 파일 (다른 이름 포함)을 검사하고 첫 번째 항목을 가리키는 심볼릭 링크로 바꾸는 방법을 찾으려고합니다. 나는 시도 fdupes했지만 그 중복을 나열합니다.
그 맥락은 다음과 같습니다. 나는 원하는대로 아이콘 테마를 사용자 정의하고 있으며, 많은 아이콘은 부모 폴더 내에서 이름과 위치가 다르고 다른 목적으로 사용 되더라도 기본적으로 동일하다는 것을 알았습니다. 그림. 하나만 필요로 할 때 동일한 수정을 20 회 또는 30 회 적용하는 것은 중복되므로 하나의 이미지 만 유지하고 다른 이미지는 모두 심볼릭 링크로 만들고 싶습니다.

예를 들어, fdupes -r ./디렉토리 안에서 실행 testdir하면 다음 결과가 나에게 반환 될 수 있습니다.

./file1.png
./file2.png
./subdir1/anotherfile.png
./subdir1/subdir2/yetanotherfile.png

이 출력이 주어지면 파일 만 유지 file1.png하고 다른 모든 파일 을 삭제 하고 파일을 가리키는 심볼릭 링크로 바꾸고 모든 원래 파일 이름 을 유지하고 싶습니다 . 따라서 file2.png이름은 유지되지만 file1.png중복되는 대신 링크가 됩니다.

이러한 링크는 절대 경로를 가리켜서는 안되지만 부모 testdir디렉토리와 관련이 있어야합니다 . 즉 yetanotherfile.png을 가리 키지 ../../file1.png않고/home/testuser/.icons/testdir/file1.png

GUI와 CLI가 포함 된 솔루션에 관심이 있습니다. fdupes내가 아는 도구이기 때문에 인용 한 것을 반드시 사용해야 하는 것은 아니지만 다른 도구를 사용하는 솔루션에도 열려 있습니다.

나는이 모든 것을 처리하는 bash 스크립트가 만들기가 어렵지 않아야한다고 확신하지만 직접 작성하는 방법을 알기에 충분하지 않습니다.

답변:


3

먼저; 일반적인 하드 링크가 아닌 심볼릭 링크를 사용해야하는 이유가 있습니까? 상대 경로가있는 심볼릭 링크의 필요성을 이해하는 데 어려움을 겪고 있습니다. 이 문제를 해결하는 방법은 다음과 같습니다.

나는 데비안 (우분투) 버전의 fdupes가 -L옵션을 사용하여 복제본을 하드 링크로 대체 할 수 있다고 생각 하지만, 이것을 확인하기 위해 데비안 설치가 없습니다.

-L옵션 이있는 버전이 없다면 commandlinefu 에서 찾은이 작은 bash 스크립트를 사용할 수 있습니다 .
이 구문은 bash에서만 작동합니다.

fdupes -r -1 path | while read line; do master=""; for file in ${line[*]}; do if [ "x${master}" == "x" ]; then master=$file; else ln -f "${master}" "${file}"; fi; done; done

위의 명령은 "path"에서 모든 중복 파일을 찾아 하드 링크로 대체합니다. ls -ilRinode 번호 를 실행 하고 확인하여이를 확인할 수 있습니다 . 다음은 10 개의 동일한 파일이있는 샘플입니다.

$ ls -ilR

total 20
3094308 -rw------- 1 username group  5 Sep 14 17:21 file
3094311 -rw------- 1 username group  5 Sep 14 17:21 file2
3094312 -rw------- 1 username group  5 Sep 14 17:21 file3
3094313 -rw------- 1 username group  5 Sep 14 17:21 file4
3094314 -rw------- 1 username group  5 Sep 14 17:21 file5
3094315 drwx------ 1 username group 48 Sep 14 17:22 subdirectory

./subdirectory:
total 20
3094316 -rw------- 1 username group 5 Sep 14 17:22 file
3094332 -rw------- 1 username group 5 Sep 14 17:22 file2
3094345 -rw------- 1 username group 5 Sep 14 17:22 file3
3094346 -rw------- 1 username group 5 Sep 14 17:22 file4
3094347 -rw------- 1 username group 5 Sep 14 17:22 file5

모든 파일에는 별도의 inode 번호가 있으므로 별도의 파일이됩니다. 이제 중복을 제거하십시오.

$ fdupes -r -1 . | while read line; do j="0"; for file in ${line[*]}; do if [ "$j" == "0" ]; then j="1"; else ln -f ${line// .*/} $file; fi; done; done
$ ls -ilR
.:
total 20
3094308 -rw------- 10 username group  5 Sep 14 17:21 file
3094308 -rw------- 10 username group  5 Sep 14 17:21 file2
3094308 -rw------- 10 username group  5 Sep 14 17:21 file3
3094308 -rw------- 10 username group  5 Sep 14 17:21 file4
3094308 -rw------- 10 username group  5 Sep 14 17:21 file5
3094315 drwx------  1 username group 48 Sep 14 17:24 subdirectory

./subdirectory:
total 20
3094308 -rw------- 10 username group 5 Sep 14 17:21 file
3094308 -rw------- 10 username group 5 Sep 14 17:21 file2
3094308 -rw------- 10 username group 5 Sep 14 17:21 file3
3094308 -rw------- 10 username group 5 Sep 14 17:21 file4
3094308 -rw------- 10 username group 5 Sep 14 17:21 file5

이제 파일의 아이 노드 번호가 모두 동일하므로 디스크의 동일한 물리적 데이터를 가리 킵니다.

이것이 당신의 문제를 해결하거나 적어도 올바른 방향으로 당신을 가리 키기를 바랍니다!


듀폰 을 링크로 대체 할 수있는 옵션 인 @arnefm 을 회상 했지만 사람 에게는 아무것도 보이지 않으며 v1.51(Ubuntu 14.04.2 LTS) 의 옵션도 아닙니다 .
Alastair

내 포크 jdupes에서 github.com/jbruchon/jdupes는-L중복 세트의 원하는 하드 연결을 수행 옵션을 선택합니다.
Jody Lee Bruchon

방금 스크립트를 조정했습니다. 여전히 공백을 처리하지 않지만 다른 특수 문자 (파일에 URL 쿼리 문자열이 있음)를 처리합니다. 또한, 그 ${line//…/}부분은 저를 위해 작동하지 않았으므로 첫 번째 "마스터"파일을 하드 링크로 가져 오는 더 깔끔한 방법을 사용했습니다.
IBBoard

1
rsync다른 종류의 파일 시스템에 사용 하는 경우 상대 소프트 링크가 필요 합니까? 또는 파일 시스템이 계층을 유지하지 않는 경우 (예 : 모든 것을 포함하는 백업 서버) /«machine-name»/...? 또는 백업에서 복원하려는 경우? 여기서 하드 링크가 어떻게 보존 될지 알 수 없습니다. 상대적인 소프트 링크는 생존 가능성이 더 높다고 생각합니다.
버디

6

많은 스크립팅을 좋아하지 않는다면 rdfind 를 추천 할 수 있습니다 . 주어진 디렉토리에서 중복 파일이 있는지 스캔하고 하드 링크 또는 소프트 링크로 연결합니다. Ruby gems 디렉토리를 중복 제거하는 데 큰 성공을 거두었습니다. 데비안 / 우분투에서 사용할 수 있습니다.


4

비슷한 상황이 있었지만 제 경우에는 심볼릭 링크가 상대 경로를 가리켜 야 하므로이 파이썬 스크립트 를 작성 하여 트릭을 수행하십시오.

#!/usr/bin/env python
# Reads fdupes(-r -1) output and create relative symbolic links for each duplicate
# usage: fdupes -r1 . | ./lndupes.py

import os
from os.path import dirname, relpath, basename, join
import sys

lines = sys.stdin.readlines()

for line in lines:
    files = line.strip().split(' ')
    first = files[0]
    print "First: %s "% first
    for dup in files[1:]:
        rel = os.path.relpath(dirname(first), dirname(dup))
        print "Linking duplicate: %s to %s" % (dup, join(rel,basename(first)))
        os.unlink(dup)
        os.symlink(join(rel,basename(first)), dup)

각 입력 행 (파일 목록)에 대해 스크립트는 파일 목록 (공백으로 구분)을 분할하고 각 파일에서 첫 번째 파일로의 상대 경로를 가져온 다음 심볼릭 링크를 만듭니다.


1

따라서 arnefm (인터넷 전체에 복사 됨)이 제공 한 답변은 파일 이름의 공백을 처리하지 않습니다. 파일의 공백을 처리하는 스크립트를 작성했습니다.

#!/bin/bash
fdupes -r -1 CHANGE_THIS_PATH | sed -e 's/\(\w\) /\1|/g' -e 's/|$//' > files
while read line; do
        IFS='|' read -a arr <<< "$line"
        orig=${arr[0]}
        for ((i = 1; i < ${#arr[@]}; i++)); do
                file="${arr[$i]}"
                ln -sf "$orig" "$file"
        done 
done < files

이것이하는 일은 속임수를 찾아서 'files'라는 파일로 구분 된 PIPE를 작성하는 것입니다.

그런 다음 파일을 한 줄씩 배열로 다시 읽고 배열의 각 요소는 PIPE로 구분됩니다.

그런 다음 배열의 첫 번째가 아닌 모든 요소를 ​​반복하여 파일을 첫 번째 요소에 대한 심볼릭 링크로 바꿉니다.

fdupes 명령이 서브 쉘에서 실행되면 외부 파일 ( 'files')을 제거 할 수 있습니다.


2
이 버전은 파이프가 포함 된 이름을 가진 파일을 처리합니까? 나는 어떤 버전도 줄 바꿈을 포함하는 파일 이름을 처리한다고 가정하지 않지만 다른 것보다는 fdupes의 제한 사항입니다.
dhag

그렇지 않지만 IFS를 원하는대로 설정할 수 있으며 (sed 교체의 값도 수정) 아무런 문제가 없어야합니다 (IFS를 'ñ'로 설정하거나 작동해야 함)
David Ventura

이로 인해 깨진 심볼릭 링크가 만들어지고 파일이 자신에게 연결되어 있습니다. 사용하지 마십시오
MrMesees

0

몇 가지주의 사항 :

  • 배쉬 특정
  • 파일 이름에 공백이 없습니다
  • 각 줄에 최대 2 개의 파일이 있다고 가정합니다.

fdupes -1r common/base/dir | while read -r -a line ; do ln -sf $(realpath --relative-to ${line[1]} ${line[0]}) ${line[1]}; done

두 개 이상의 파일이 중복 된 경우 (예 : file1 file2 file3) 각 쌍에 대해 심볼릭 링크를 만들어야합니다. file1, file2 및 file1, file3을 2 개의 개별 사례로 취급하십시오.

if [[ ${#line[@]} -gt 2 ]] ;then 
  ln -sf $(realpath --relative-to ${line[1]} ${line[0]}) ${line[1]} 
  ln -sf $(realpath --relative-to ${line[2]} ${line[0]}) ${line[2]} 
  ...
fi

라인 당 임의의 수의 복제본을 자동으로 처리하기 위해 이것을 소비하면 약간의 노력이 필요합니다.

다른 방법은 먼저 절대 경로에 대한 심볼릭 링크를 만든 다음 변환하는 것입니다.

fdupes -1r /absolute/path/common/base/dir | while read -r -a line ; do ln -sf ${line[0]} ${line[1]}; done
chroot /absolute/path/common/base/dir ; symlinks -cr .

이것은 @Gilles의 답변을 기반으로합니다 : /unix//a/100955/77319

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