"https://github.com/taltman/scripts/blob/master/unix_utils/find-dupes.awk"를 Linux/Ubuntu로 포팅

"https://github.com/taltman/scripts/blob/master/unix_utils/find-dupes.awk"를 Linux/Ubuntu로 포팅

질문: 중복 파일을 찾는 속도가 빨라야 하며 파일을 열고 해싱할 필요가 없습니다. @taltman은 훌륭하고 빠른 스크립트를 작성했습니다.여기, MD5는 동일한 크기의 파일이 발견된 경우에만 사용됩니다. 이 스크립트는 Centos에서만 실행됩니다. 그리고 가장 큰 파일에 대한 출력을 제공하지 않습니다.

상태: Cinnamon Mint에서 실행되도록 스크립트를 포팅했습니다. 파일 이름의 공백도 처리할 수 있습니다. 그것은여기. 이제 다음을 출력합니다.

MD5 30599d19eb93cfb45030a1b0270e698c:
        ./../abc.jpg
        ./../xyz.jpg
MD5 3d0bc4e9ec8c77f5a430d8455252ef58:
        ./../def.mp4
        ./../hij.mp4

보고서 블록에 크기(아래쪽이 가장 큼)별로 정렬을 추가하고 크기를 표시하고 싶습니다. 출력은 내가 좋아하는 것과 같습니다.

## 4.53MB (MD5 30599d19eb93cfb45030a1b0270e698c):
        ./../abc.jpg
        ./../xyz.jpg
## 1.76GB (MD5 3d0bc4e9ec8c77f5a430d8455252ef58):
        ./../def.mp4
        ./../hij.mp4

도움을 요청: AWK를 정말로 이해하는 사람이 있나요? 도움을 주고 싶으신가요?

답변1

귀하의 질문에 대한 답변은 아닐 수도 있지만 구현하기가 더 쉬울 수도 있습니다. 나는 그것을 bash로 작성했는데 아마도 awk를 다루는 것보다 쉬울 것입니다.

#!/usr/bin/env bash

die()
{
  echo >&2 "$@"
  exit 1
}

usage()
{
  echo >&2 "Usage: $0 path"
  die
}

checkdupes() {
  local path="$1"
  declare -A flist
  declare -a output_array

  while read -r sum fname; do
    if [[ ${flist[$sum]} ]]; then
      fsize=$(stat --printf="%s" "$fname")
      fsize_converted=$(convert_bytes "$fsize")
      output_array+=("$fsize_converted $(md5sum "$fname") and ${flist[$sum]} are identical")
    fi
    flist[$sum]+="$fname"
  done < <(find "$path" -type f -exec sha256sum {} +)

  IFS=$'\n' sorted_array=($(sort -h <<<"${output_array[*]}"))
  unset IFS
  for ((i=${#sorted_array[@]}-1; i>=0; i--)); do
    printf '%s\n' "${sorted_array[i]}"
  done
}

convert_bytes() {
  local bytes=$1
  local unit=""
  local value=""

  if ((bytes < 1024)); then
    unit="B"
    value=$bytes
  elif ((bytes < 1048576)); then
    unit="KB"
    value=$((bytes / 1024))
  elif ((bytes < 1073741824)); then
    unit="MB"
    value=$((bytes / 1048576))
  else
    unit="GB"
    value=$((bytes / 1073741824))
  fi

  printf '%d%s' "${value}" "${unit}"
}

if (($# < 1)); then
  usage
else
  checkdupes "$1"
fi

내 SE 스크립트에서 이 부분을 볼 수 있습니다.

die()
{
  echo >&2 "$@"
  exit 1
}

usage()
{
  echo >&2 "Usage: $0 path"
  die
}

이것은 실제로 오류 처리 부분입니다. 라는 파일을 생성하여 errorhandling 스크립트에서 가져올 수 있습니다.

용법:

./check_dupes [path]

도움이 되었기를 바랍니다!

답변2

해결책은 다음과 같습니다.TXR 불분명한 음성

하위 디렉터리에 여러 개의 중복 파일을 만들었습니다 linenoise.

$ txr findup.tl linenoise/
---
969d22f22e167313   1c11d2 (linenoise/history.txt.link linenoise/history.txt)
---
c3211c8f2a6ac412   1c1e0d (linenoise/example.c)
c3211c8f2a6ac412   1cd21f (linenoise/example.c.dup)
---
e4cd0181a0e73fda   1cd20a (linenoise/LICENSE.lnk linenoise/LICENSE.dup)
e4cd0181a0e73fda   1c11d4 (linenoise/LICENSE)

프로그램을 실행하면 앞에 ---.

프로그램은 서로 하드 링크된 파일(동일한 inode 번호)과 해싱 또는 둘의 조합을 통해 중복된 파일을 식별합니다.

위에는 다양한 상황이 나와 있습니다.

파일이 history.txt.link서로 history.txt연결되어 있습니다. 다른 중복 항목이 없으므로 행 하나만 표시됩니다.

이러한 파일 example.c과 파일은 example.c.dup동일하지만 개체가 다릅니다.

그런 다음 혼합된 상황이 발생합니다. LICENSE.lnkLICENSE.dup은 동일한 객체에 대한 링크이고 해당 객체는 의 복제본입니다 LICENSE.

암호:

(let ((dir "."))
  (match-case *args*
    ((@where) (set dir where))
    (())
    (@else (put-line "bad arguments" *stderr*)
           (exit nil)))
  (flow (build (ftw dir (lambda (path type stat . rest)
                          (if (eql type ftw-f)
                            (add stat)))))
    (group-by .size)
    hash-values
    (keep-if cdr)
    (each ((group @1))
      (flow group
        (group-by .ino)
        hash-values
        (collect-each ((group @1))
          (let ((hash (with-stream (s (open-file (car group).path))
                        (sha256-stream s))))
            (cons hash group)))
        (sort-group @1 car)
        (each ((subgr @1))
          (when-match @(require ((@hash @stat . @more-stats) . @other-members)
                                (or other-members more-stats))
                      subgr
            (put-line "---")
            (each-match ((@nil . @stats) subgr)
              (format t "~x ~08x ~a\n"
                      [hash 0..8] (car stats).ino
                      (mapcar .path stats)))))))))

ftw함수는 nftwPOSIX 함수를 둘러싼 래퍼입니다. 구조(Lisp 버전 ) lambda를 포함하여 액세스된 모든 객체에 대한 콜백 정보를 제공합니다 . 구조 에는 (인덱스 노드 번호) 및 (전체 상대 경로) 와 같은 슬롯이 있습니다 . 우리는 이 객체들로 필요한 모든 것을 할 수 있습니다.statstruct statstatinosizepathstat

먼저 개체를 크기별로 그룹화하고 구성원이 두 명 이상인 그룹만 유지합니다. 질문에 나와 있듯이 고유한 크기의 파일이 중복되지 않습니다.

이 방법은 먼저 동일한 크기의 경로 그룹을 찾습니다.

그룹을 반복하고 각 그룹을 inode 번호별로 하위 그룹으로 그룹화합니다. 그런 다음 각 그룹의 리더(Lisp 구조체 목록 )를 stat해시 하고 해당 해시를 헤더 항목으로 그룹에 추가합니다.

마지막으로 우리는 sort-group이러한 그룹을 해싱하여 작업합니다. 이는 그룹이 해시별로 정렬되고 중복된 해시가 함께 그룹화됨을 의미합니다.

그런 다음 동일한 해시 그룹을 반복하고 덤프하면 됩니다. 두 개 이상의 구성원(중복 개체)이 있는 그룹이나 여러 경로가 있는 inode만 보고하도록 주의해야 합니다 (or other-members more-stats).

코드를 개선할 수 있습니다. 주어진 크기의 모든 파일이 동일한 개체(동일한 inode)에 연결되면 해당 파일에 대한 해시를 계산할 필요가 없으며 해당 파일이 동일하고 해당 하위 트리에 복사본이 없다는 것을 알 수 있습니다. 해시를 가짜 값(예: )으로 대체 0한 다음 반복 할 수 있습니다 sort-group.

또한 프로그램은 오탐지를 제거하기 위한 포괄적인 비교 수행을 무시합니다. 동일한 파일이 아닌 동일한 SHA256을 사용하여 파일을 보고합니다.

다음은 순수 하드 링크 중복의 해시를 제거하는 한 가지 가능한 방법입니다.

        (collect-each ((group @1))
          (let ((hash (if (cdr @1)
                        (with-stream (s (open-file (car group).path))
                          (sha256-stream s))
                        (load-time (make-buf 32)))))
            (cons hash group)))

그러면 출력은 다음과 같습니다.

---
0000000000000000   1c11d2 (linenoise/history.txt.link linenoise/history.txt)
---
c3211c8f2a6ac412   1c1e0d (linenoise/example.c)
c3211c8f2a6ac412   1cd21f (linenoise/example.c.dup)
---
e4cd0181a0e73fda   1cd20a (linenoise/LICENSE.lnk linenoise/LICENSE.dup)
e4cd0181a0e73fda   1c11d4 (linenoise/LICENSE)

나는 이 사례를 모두 0으로 이루어진 완전한 해시로 대체했는데, 이는 명확하게 보입니다. (load-time (make-buf 32))SHA256과 동일한 길이의 32바이트 올제로 버퍼를 생성합니다. load-time컴파일된 코드에서는 계산이 실행될 때마다가 아니라 코드가 로드될 때 한 번 수행되는지 확인하세요. 이 cdr함수는 "이 목록에 두 개 이상의 항목이 있습니까?"를 의미하는 Lisp 관용어입니다. 첫 번째 항목을 제외한 목록의 나머지 부분을 검색합니다. 비어 있으면 nil부울 false인 를 반환합니다.

답변3

내 생각에 GNU 도구를 사용하여 수행하려는 작업은 다음과 같을 수 있습니다(테스트되지 않음).

while IFS= read -r -d $'\0' currName; do
    currSum=$(md5sum "$currName")
    if [[ "$currSum" == "$prevSum" ]]; then
        printf 'Dups:'
        printf '%s\n' "$prevName"  # end with \0 if necessary
        printf '%s\n' "$currName"
    fi
    prevSum="$currSum"
    prevName="$currName"
done < <(
    find . -type f -printf '%s\t%p\0' |
    sort -z -k2- |
    awk '
        BEGIN { RS=ORS="\0" }
        {
            currName = $0
            sub(/[^\t]+\t/,"",currName)
        }
        $1 == prevSize {
            print prevName currName
            prevName = ""
            next
        }
        {
            prevSize = $1
            prevName = currName ORS
        }
    '
)

관련 정보