同じファイルをハードリンクに変換

同じファイルをハードリンクに変換

私は品質上の理由で、元の購入形式に関係なく、ディレクトリの下のツリーに多くの音楽を保持します。同様の構造を持つ2番目のディレクトリツリーがありますが、すべてのファイルは損失圧縮されているため、携帯電話で再生でき、メタデータが時々変わります(スペースを節約するために埋め込まれたカバーを削除するなど)。

私は音楽のかなりの部分について、2つのケースの間に違いはないことがわかりました。通常、配布バージョンはmp3 / ogg形式でのみ提供され、挿入されたカバーアートがない場合です。ハードドライブのスペースは安いかもしれませんが、それでも無駄にする理由はありません。スクリプトを書く方法はありますか?

  1. 両方のディレクトリに同じファイルがあることを確認する
  2. 同じファイルが見つかるたびに、あるファイルを別のファイルへのハードリンクに置き換えます。
  3. たとえば、時間を節約するために全体の違いを特定するのに時間を費やす必要はありません。
  4. しかし、2つの異なるファイルのコピーを誤って削除するリスクはまだありません。ハッシュだけを比較すると、これはリモートですが、可能性がゼロではありません。

答え1

次のコマンドは、md5現在のディレクトリまたは次のディレクトリ内のすべてのファイルのMD5ダイジェストを生成するために使用されます。

find . -type f -exec md5 {} +

BSDユーティリティがない場合は交換してくださいmd5md5sum --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

これは、コマンドラインで2つのディレクトリを使用して、そのディレクトリ内の(または指す場所)ディレクトリごとに1つずつ、md5.1andというファイルを生成します。ファイルはMD5ダイジェストでソートされます。md5.2/tmp$TMPDIR

このファイルは次のとおりです。

MD5 (<path>) = <MD5 digest>

すべてのファイルには次の行があります。

次に、同じスクリプトで2つのファイル間のチェックサムを比較します。

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

これは、チェックサムを結合フィールドとして使用して、2つのファイル間のリレーショナル「結合」操作を実行します。両方のフィールドで同じチェックサム行をマージして出力します。

両方のファイルのチェックサムが同じ場合は、次のように出力されます。

<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集約的な作業です。

ファイルの長さが異なる場合にのみデータを比較する必要があります。指定された長さのファイルが1つしかない場合、明らかに重複ファイルはありません。 2つある場合は、単に比較するのがハッシュよりも常に効率的です。 3つ以上の場合、比較数は増えますが、最初のバイトまたはブロックが異なる可能性が高いため、ディスクI / Oは依然として低く、繰り返し読み取りはキャッシュから返されます。

そのため、私は再帰的なディレクトリリストを作成し、長さ+パス名のリストを準備し、リストを数字でソートし、最後に同じ長さを共有するファイルセットのみを処理するようにペアごとの比較を実行することをお勧めします。 2 つのファイルが一致する場合は、そのいずれかをハードリンクに置き換えることができます。

関連情報