「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接続されています。他の重複エントリがないため、1行だけが表示されます。

これらのファイル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

まず、オブジェクトをサイズ別にグループ化し、2人以上のメンバーを持つグループのみを保持します。質問に示すように、固有のサイズのファイルは重複しません。

この方法では、まず同じサイズのルートグループを探します。

グループを繰り返し、各グループを inode 番号でサブグループにグループ化します。次に、各グループのリーダー(Lisp構造体のリスト)をstatハッシュし、そのハッシュをヘッダー項目としてグループに追加します。

最後に、私たちはsort-groupこれらのグループをハッシュして作業します。これは、グループがハッシュごとにソートされ、重複したハッシュが一緒にグループ化されることを意味します。

その後、同じハッシュグループを繰り返しダンプします。複数のメンバー(重複オブジェクト)を持つグループ、または複数のパスを持つノードのみを報告するように注意する必要があります(or other-members more-stats)

コードを改善できます。特定のサイズのすべてのファイルが同じオブジェクト(同じinode)にリンクされている場合は、そのファイルのハッシュを計算する必要はなく、そのファイルが同じで、そのサブツリーにコピーがないことがわかります。ハッシュを偽の値(たとえば)に置き換えてから0繰り返すことができますsort-group

さらに、プログラムは誤検出を排除するための包括的な比較実行を無視します。同じファイルではなく、同じSHA256を使用してファイルを報告します。

以下は、純粋なハードリンク冗長性のハッシュを削除する1つの可能な方法です。

        (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))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
        }
    '
)

関連情報