これは、 bashで実装された「単純な」ソリューションです (2 つの外部コマンドを除いて:md5sum
もちろん、stat
ユーザーの快適さのためにのみ使用され、アルゴリズムの一部ではありません)。このことは、100% Bash のクイックソートを実装しています (私はそれを誇りに思っています)。
#!/bin/bash
# Finds similar (based on md5sum) files (recursively) in given
# directory. If several files with same md5sum are found, sort
# them by modified (most recent first) and prompt user for deletion
# of the oldest
die() {
printf >&2 '%s\n' "$@"
exit 1
}
quicksort_files_by_mod_date() {
if ((!$#)); then
qs_ret=()
return
fi
# the return array is qs_ret
local first=$1
shift
local newers=()
local olders=()
qs_ret=()
for i in "$@"; do
if [[ $i -nt $first ]]; then
newers+=( "$i" )
else
olders+=( "$i" )
fi
done
quicksort_files_by_mod_date "${newers[@]}"
newers=( "${qs_ret[@]}" )
quicksort_files_by_mod_date "${olders[@]}"
olders=( "${qs_ret[@]}" )
qs_ret=( "${newers[@]}" "$first" "${olders[@]}" )
}
[[ -n $1 ]] || die "Must give an argument"
[[ -d $1 ]] || die "Argument must be a directory"
dirname=$1
shopt -s nullglob
shopt -s globstar
declare -A files
declare -A hashes
for file in "$dirname"/**; do
[[ -f $file ]] || continue
read md5sum _ < <(md5sum -- "$file")
files[$file]=$md5sum
((hashes[$md5sum]+=1))
done
has_found=0
for hash in "${!hashes[@]}"; do
((hashes[$hash]>1)) || continue
files_with_same_md5sum=()
for file in "${!files[@]}"; do
[[ ${files[$file]} = $hash ]] || continue
files_with_same_md5sum+=( "$file" )
done
has_found=1
echo "Found ${hashes[$hash]} files with md5sum=$hash, sorted by modified (most recent first):"
# sort them by modified date (using quicksort :p)
quicksort_files_by_mod_date "${files_with_same_md5sum[@]}"
for file in "${qs_ret[@]}"; do
printf " %s %s\n" "$(stat --printf '%y' -- "$file")" "$file"
done
read -p "Do you want to remove the oldest? [yn] " answer
if [[ ${answer,,} = y ]]; then
echo rm -fv -- "${qs_ret[@]:1}"
fi
done
if((!has_found)); then
echo "Didn't find any similar files in directory \`$dirname'. Yay."
fi
スクリプトは自明だと思います(ストーリーのように読むことができます)。これは私が知っているベスト プラクティスを使用しており、ファイル名に不適切な文字 (スペース、改行、ハイフンで始まるファイル名、改行で終わるファイル名など) が含まれていても 100% 安全です。
これは bash のグロブを使用するため、肥大化したディレクトリ ツリーがある場合は少し遅くなる可能性があります。
いくつかのエラー チェックがありますが、多くは欠落しているため、本番環境ではそのまま使用しないでください。(これらを追加するのは簡単ですが、かなり面倒です)。
アルゴリズムは次のとおりです。指定されたディレクトリ ツリー内の各ファイルをスキャンします。ファイルごとに、その md5sum を計算し、連想配列に格納します。
files
キーはファイル名、値は md5sums です。
hashes
キーを使用すると、ハッシュと値がファイルの数になり、その md5sum がキーになります。
これが完了したら、見つかったすべての md5sum をスキャンし、複数のファイルに対応するものだけを選択してから、この md5sum を持つすべてのファイルを選択し、変更日でそれらをクイックソートして、ユーザーにプロンプトを表示します。
重複が見つからない場合の効果: スクリプトはユーザーに適切に通知します。
これが最も効率的な方法だとは言えませんが (たとえば、Perl の方が優れているかもしれません)、とても楽しく、驚くほど読みやすく、従うことができ、勉強することで多くのことを学べる可能性があります!
bash バージョン ≥ 4 にのみあるいくつかのバシズムと機能を使用します。
お役に立てれば!
述べる。システムdate
にスイッチがある場合は、コマンドを次-r
のように置き換えることができます。stat
date -r "$file"
述べる。echo
の前を離れましたrm
。スクリプトの動作に問題がなければ削除してください。次に、3 つの外部コマンドを使用するスクリプトが作成されます:)
。