주어진 디렉토리에서 중복 파일 (다른 이름 포함)을 검사하고 첫 번째 항목을 가리키는 심볼릭 링크로 바꾸는 방법을 찾으려고합니다. 나는 시도 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 스크립트가 만들기가 어렵지 않아야한다고 확신하지만 직접 작성하는 방법을 알기에 충분하지 않습니다.
답변
먼저; 일반적인 하드 링크가 아닌 심볼릭 링크를 사용해야하는 이유가 있습니까? 상대 경로가있는 심볼릭 링크의 필요성을 이해하는 데 어려움을 겪고 있습니다. 이 문제를 해결하는 방법은 다음과 같습니다.
나는 데비안 (우분투) 버전의 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 -ilR
inode 번호 를 실행 하고 확인하여이를 확인할 수 있습니다 . 다음은 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
이제 파일의 아이 노드 번호가 모두 동일하므로 디스크의 동일한 물리적 데이터를 가리 킵니다.
이것이 당신의 문제를 해결하거나 적어도 올바른 방향으로 당신을 가리 키기를 바랍니다!
답변
많은 스크립팅을 좋아하지 않는다면 rdfind 를 추천 할 수 있습니다 . 주어진 디렉토리에서 중복 파일이 있는지 스캔하고 하드 링크 또는 소프트 링크로 연결합니다. Ruby gems 디렉토리를 중복 제거하는 데 큰 성공을 거두었습니다. 데비안 / 우분투에서 사용할 수 있습니다.
답변
비슷한 상황이 있었지만 제 경우에는 심볼릭 링크가 상대 경로를 가리켜 야 하므로이 파이썬 스크립트 를 작성 하여 트릭을 수행하십시오.
#!/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)
각 입력 행 (파일 목록)에 대해 스크립트는 파일 목록 (공백으로 구분)을 분할하고 각 파일에서 첫 번째 파일로의 상대 경로를 가져온 다음 심볼릭 링크를 만듭니다.
답변
따라서 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 -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