将“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)

当我运行该程序时,它报告重复的组,每个组前面都有---.在每个组中,它打印 SHA256 的一部分,后跟 inode 号,然后是路径。

该程序通过哈希或两者的组合来识别彼此硬链接(相同 inode 号)以及重复项的文件。

上面显示了各种情况。

文件history.txt.linkhistory.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函数是 POSIXnftw函数的包装器。它为我们提供了lambda有关每个访问对象的回调信息,包括stat结构(Lisp 版本struct stat)。该stat结构具有诸如ino(索引节点号)、sizepath(完整相对路径)之类的槽。我们可以用这些stat对象做我们需要做的一切。

我们首先按大小对对象进行分组,并仅保留具有两个或更多成员的组。正如问题所指出的,具有唯一大小的文件没有重复项。

该方法首先找到具有相同大小的路径组。

我们迭代这些组;我们按 inode 编号将每个组分组为子组。然后,我们对每个组的领导者(这是一个 Lispstat结构列表)进行哈希处理,并将哈希值作为头项附加到该组上。

最后,我们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)

我用一个完整的全零哈希来代替这种情况,这是清晰可见的。(load-time (make-buf 32))创建一个 32 字节全零缓冲区,长度与 SHA256 相同。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
        }
    '
)

相关内容