測試

測試

我想在目錄中找到重複的文件,然後刪除除一個之外的所有文件,以回收空間。如何使用 shell 腳本實現此目的?

例如:

pwd
folder

裡面的文件有:

log.bkp
log
extract.bkp
extract

我需要將 log.bkp 與所有其他文件進行比較,如果發現重複文件(按其內容),我需要將其刪除。同樣,文件「log」必須與後面的所有其他文件一起檢查,依此類推。

到目前為止,我已經寫了這個,但它沒有給出預期的結果。

#!/usr/bin/env ksh
count=`ls -ltrh /folder | grep '^-'|wc -l`
for i in `/folder/*`
do
   for (( j=i+1; j<=count; j++ ))
   do
      echo "Current two files are $i and $j"
      sdiff -s $i  $j
      if [ `echo $?` -eq  0 ]
      then
         echo "Contents of $i and $j are same"
       fi
    done
 done

答案1

如果您願意簡單地使用命令列工具,而不必建立 shell 腳本,則fdupes大多數發行版上都可以使用該程式來執行此操作。

還有fslint具有相同功能的基於 GUI 的工具。

答案2

此解決方案將在 O(n) 時間內找到重複項。每個文件都有一個為其產生的校驗和,並且每個文件依次透過關聯數組與一組已知校驗和進行比較。

#!/bin/bash
#
# Usage:  ./delete-duplicates.sh  [<files...>]
#
declare -A filecksums

# No args, use files in current directory
test 0 -eq $# && set -- *

for file in "$@"
do
    # Files only (also no symlinks)
    [[ -f "$file" ]] && [[ ! -h "$file" ]] || continue

    # Generate the checksum
    cksum=$(cksum <"$file" | tr ' ' _)

    # Have we already got this one?
    if [[ -n "${filecksums[$cksum]}" ]] && [[ "${filecksums[$cksum]}" != "$file" ]]
    then
        echo "Found '$file' is a duplicate of '${filecksums[$cksum]}'" >&2
        echo rm -f "$file"
    else
        filecksums[$cksum]="$file"
    fi
done

如果您未在命令列上指定任何檔案(或通配符),它將使用目前目錄中的檔案集。它將比較多個目錄中的文件,但不會遞歸到目錄本身。

該集中的“第一個”文件始終被視為最終版本。不考慮文件時間、權限或所有權。僅考慮內容。

當您確定它能滿足您的要求時,請將其echo從行中刪除。rm -f "$file"請注意,如果您要替換該行,則ln -f "${filecksums[$cksum]}" "$file"可以硬連結內容。同樣節省磁碟空間,但不會遺失檔案名稱。

答案3

腳本中的主要問題似乎是i將實際檔案名稱作為值,而j只是一個數字。將名稱放入數組並使用ij作為索引應該可以工作:

files=(*)
count=${#files[@]}
for (( i=0 ; i < count ;i++ )); do 
    for (( j=i+1 ; j < count ; j++ )); do
        if diff -q "${files[i]}" "${files[j]}"  >/dev/null ; then
            echo "${files[i]} and ${files[j]} are the same"
        fi
    done
done

(似乎可以與 Bash 和ksh/ ksh93Debian 一起使用。)

此賦值運算將使用兩個元素和(索引為 0 和 1)來a=(this that)初始化陣列。分詞和通配符照常工作,因此使用目前目錄中所有檔案的名稱(點檔案除外)進行初始化。將擴展到數組的所有元素,哈希符號要求長度,數組中元素的數量也是如此。 (請注意,這將是數組的第一個元素,並且是第一個元素的長度,而不是數組!)athisthatfiles=(*)files"${files[@]}"${#files[@]}${files}${#files}

for i in `/folder/*`

這裡的反引號肯定是一個錯字嗎?您將作為命令運行第一個文件,並將其餘文件作為參數提供給它。

答案4

順便說一句,使用校驗和或雜湊是個好主意。我的腳本沒有使用它。但如果檔案很小且檔案數量不大(例如 10-20 個檔案),則此腳本將運行得相當快。如果你有 100 個或更多文件,每個文件有 1000 行,那麼時間將超過 10 秒。

用法: ./duplicate_removing.sh files/*

#!/bin/bash

for target_file in "$@"; do
    shift
    for candidate_file in "$@"; do
        compare=$(diff -q "$target_file" "$candidate_file")
        if [ -z "$compare" ]; then
            echo the "$target_file" is a copy "$candidate_file"
            echo rm -v "$candidate_file"
        fi
    done
done

測試

建立隨機檔案: ./creating_random_files.sh

#!/bin/bash

file_amount=10
files_dir="files"

mkdir -p "$files_dir"

while ((file_amount)); do
    content=$(shuf -i 1-1000)
    echo "$RANDOM" "$content" | tee "${files_dir}/${file_amount}".txt{,.copied} > /dev/null
    ((file_amount--))
done

跑步 ./duplicate_removing.sh files/* 並得到輸出

the files/10.txt is a copy files/10.txt.copied
rm -v files/10.txt.copied
the files/1.txt is a copy files/1.txt.copied
rm -v files/1.txt.copied
the files/2.txt is a copy files/2.txt.copied
rm -v files/2.txt.copied
the files/3.txt is a copy files/3.txt.copied
rm -v files/3.txt.copied
the files/4.txt is a copy files/4.txt.copied
rm -v files/4.txt.copied
the files/5.txt is a copy files/5.txt.copied
rm -v files/5.txt.copied
the files/6.txt is a copy files/6.txt.copied
rm -v files/6.txt.copied
the files/7.txt is a copy files/7.txt.copied
rm -v files/7.txt.copied
the files/8.txt is a copy files/8.txt.copied
rm -v files/8.txt.copied
the files/9.txt is a copy files/9.txt.copied
rm -v files/9.txt.copied

相關內容