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

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

나는 품질상의 이유로 원래 구입한 형식에 관계없이 디렉토리 아래 트리에 많은 음악을 보관합니다. 유사한 구조를 가진 두 번째 디렉터리 트리가 있지만 모든 파일은 손실 압축 상태이므로 휴대폰에서 재생할 수 있으며 메타데이터가 때때로 변경됩니다(예: 공간을 절약하기 위해 내장된 표지 제거).

나는 음악의 상당 부분에 대해 두 경우 사이에 차이가 없다는 것을 알았습니다. 일반적으로 배포 버전이 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는 여전히 낮고 반복 읽기는 캐시에서 반환됩니다.

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

관련 정보