將相同的檔案轉換為硬連結

將相同的檔案轉換為硬連結

我在一個目錄下的樹中保存了很多音樂,為了保證質量,以我最初獲取的任何格式保存。我有第二個目錄樹,其結構相似,但所有檔案均採用有損壓縮格式,可透過我的手機播放,並且偶爾會更改元資料(例如,刪除嵌入的封面以節省空間)。

我發現對於音樂的很大一部分,這兩個實例之間沒有區別 - 通常當分發版本僅以 mp3/ogg 形式提供並且沒有嵌入封面時。硬碟空間可能很便宜,但這沒有理由浪費它。有沒有辦法編寫腳本:

  1. 檢查兩個目錄中是否有相同的文件
  2. 每當找到相同的文件時,將一個文件替換為另一個文件的硬鏈接
  3. 例如,為了節省時間,無需花時間獲得完整的差異
  4. 但仍然沒有意外刪除兩個不同文件的副本的風險,如果我只是比較哈希值,這是一個遙遠但非零的機會?

答案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.1和的文件md5.2,每個目錄對應一個文件,位於/tmp(或$TMPDIR指向的任何位置)。這些文件按照 MD5 摘要排序。

這些文件看起來像

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]

在循環echowhile是為了安全。運行一次看看會發生什麼,如果您確信它正在做正確的事情,請將其刪除並再次運行它。

請記住,硬連結不能跨越分割區。這意味著兩個目錄需要位於同一分割區上。文件位於第二如果發現重複目錄將被覆蓋。將原件的備份保存在某處,直到您對結果感到滿意為止!

請注意,如果任何檔案的檔案名稱中包含(或或 製表符,則此解決方案將無法正常運作。)

答案2

除非您有大量非常相似的文件,否則計算和比較雜湊值不會加快查找重複項的過程。最慢的操作是磁碟讀取。計算哈希值意味著讀取整個文件,而且它是一項 CPU 密集型任務,具有現代加密的強哈希值。

只有當檔案長度不同時我們才必須比較資料。如果只有一個給定長度的文件,顯然不存在重複項。如果有兩個,簡單地比較它們總是比散列更有效。如果有三個或更多,比較次數會增加,但很可能它們的第一個位元組或區塊不同,因此磁碟 I/O 仍然較低,重複讀取會從快取返回。

這就是為什麼我建議製作一個遞歸目錄列表,準備一個長度+路徑名列表,然後按數字對列表進行排序,最後通過成對比較來僅處理共享相同長度的文件集。如果兩個檔案匹配,則可以用硬連結替換其中一個。

相關內容