동일한 파일을 하드링크로 변환

동일한 파일을 하드링크로 변환

한 디렉토리 아래의 트리에는 품질을 위해 처음에 어떤 형식으로든 저장한 음악이 많이 있습니다. 두 번째 디렉터리 트리는 구조가 비슷하지만 모든 파일이 손실 압축 형식으로 내 휴대폰에서 재생 가능하고 가끔씩 메타데이터가 변경됩니다(예: 공간 절약을 위해 포함된 표지 제거).

음악의 상당 부분에 대해 두 인스턴스 사이에 차이가 없다는 생각이 들었습니다. 일반적으로 배포 버전이 mp3/ogg로만 사용 가능하고 포함된 커버가 없는 경우입니다. 하드 드라이브 공간은 저렴할 수 있지만 그렇다고 해서 낭비할 이유는 없습니다. 스크립트를 작성하는 방법이 있습니까?

  1. 두 디렉터리에 동일한 파일이 있는지 확인하세요.
  2. 동일한 파일이 발견될 때마다 하나를 다른 파일에 대한 하드링크로 교체
  3. 예를 들어, 시간을 고려하여 전체 비교를 얻는 데 시간을 들이지 않고
  4. 그러나 동일하지 않은 두 파일의 복사본을 실수로 삭제할 위험은 여전히 ​​없습니다. 예를 들어 해시를 비교하는 경우에는 원격이지만 0이 아닌 가능성이 있습니까?

답변1

다음은 md5현재 디렉터리 또는 그 아래의 모든 파일에 대한 MD5 다이제스트를 생성하는 데 사용됩니다.

find . -type f -exec md5 {} +

BSD 유틸리티가 없으면 md5로 바꾸십시오 .md5sum --tagmd5

디렉터리에서 이를 수행하는 간단한 스크립트를 작성해 보겠습니다.

#!/bin/bash

tmpdir=${TMPDIR:-/tmp}

if (( $# != 2 )); then
    echo 'Expected two directories as arguments' >&2
    exit 1
fi

i=0
for dir in "$@"; do
    (( ++i ))
    find "$dir" -type f -exec md5 {} + | sort -t '=' -k2 -o "$tmpdir/md5.$i"
done

이는 명령줄에서 두 개의 디렉토리를 사용하여 (또는 가리키는 위치) 각 디렉토리에 대해 하나의 파일인 md5.1and 라는 파일을 생성합니다. 이러한 파일은 MD5 다이제스트에 정렬됩니다.md5.2/tmp$TMPDIR

파일은 다음과 같습니다

MD5 (<path>) = <MD5 digest>

각 파일에 대해 이러한 줄을 하나씩 사용합니다.

그런 다음 동일한 스크립트에서 두 파일 간의 체크섬을 비교합니다.

join -t '=' -1 2 -2 2 "$tmpdir"/md5.[12]

이는 체크섬을 결합 필드로 사용하여 두 파일 간의 관계형 "결합" 작업을 수행합니다. 두 필드에서 동일한 체크섬을 갖는 모든 라인이 병합되어 출력됩니다.

두 파일의 체크섬이 동일하면 다음이 출력됩니다.

<space><MD5 digest>=MD5 (<path1>) =MD5 (<path2>)

awk이는 두 경로를 구문 분석하기 위해 에 직접 전달될 수 있습니다 .

awk -F '[()]' 'BEGIN { OFS="\t" } { print $2, $4 }'

이는 각 줄을 및 를 -F [()]기반으로 하는 필드로 나누고 싶다는 의미입니다 . 이렇게 하면 필드 2와 4의 경로가 남습니다.()

이것은 출력됩니다

<path1><tab><path2>

그런 다음 탭으로 구분된 경로 쌍을 읽고 올바른 명령을 실행하여 링크를 생성하기만 하면 됩니다.

while IFS=$'\t' read -r path1 path2; do
    echo ln -f "$path1" "$path2"
done

요약하자면:

#!/bin/bash

tmpdir=${TMPDIR:-/tmp}

if (( $# != 2 )); then
    echo 'Expected two directories as arguments' >&2
    exit 1
fi

i=0
for dir in "$@"; do
    (( ++i ))
    find "$dir" -type f -exec md5 {} + | sort -t '=' -k2 -o "$tmpdir/md5.$i"
done

join -t '=' -1 2 -2 2 "$tmpdir"/md5.[12] |
awk -F '\\)|\\(' 'BEGIN { OFS="\t" } { print $2, $4 }' |
while IFS=$'\t' read -r path1 path2; do
    echo ln -f "$path1" "$path2"
done

rm -f "$tmpdir"/md5.[12]

echo루프 내부는 안전 while을 위해 존재합니다. 한 번 실행하여 무슨 일이 일어나는지 확인하고, 올바른 일을 하고 있다는 확신이 들면 제거하고 다시 실행하세요.

하드 링크는 파티션에 걸쳐 있을 수 없다는 점을 기억하십시오. 이는 두 디렉터리가 모두 동일한 파티션에 있어야 함을 의미합니다. 파일은두번째디렉터리가 중복된 것으로 발견되면 덮어쓰게 됩니다. 결과에 만족할 때까지 원본을 어딘가에 백업해 두세요!

파일 이름에 탭이 (있는 경우 이 해결 방법이 제대로 작동하지 않습니다 .)

답변2

매우 유사한 파일이 많이 모여 있는 경우가 아니면 해시를 계산하고 비교해도 중복 항목을 찾는 프로세스 속도가 빨라지지 않습니다. 가장 느린 작업은 디스크 읽기입니다. 해시를 계산한다는 것은 전체 파일을 읽는 것을 의미하며, 이는 현대의 암호학적으로 강력한 해시를 사용하는 CPU 집약적인 작업입니다.

파일 길이가 다른 경우에만 데이터를 비교해야 합니다. 주어진 길이의 파일이 하나만 있는 경우에는 분명히 중복 파일이 없습니다. 두 개가 있는 경우 단순히 비교하는 것이 해싱보다 항상 더 효율적입니다. 3개 이상이면 비교 횟수는 늘어나지만 첫 번째 바이트나 블록에서는 다를 가능성이 있으므로 디스크 I/O는 여전히 낮고 반복 읽기는 캐시에서 반환됩니다.

그렇기 때문에 길이+경로 이름 목록을 준비하는 재귀 디렉터리 목록을 만든 다음 목록을 숫자로 정렬하고 마지막으로 쌍으로 비교하여 동일한 길이를 공유하는 파일 집합만 처리하는 것이 좋습니다. 두 파일이 일치하면 하나는 하드링크로 대체될 수 있습니다.

관련 정보