ディレクトリ内の重複エントリの検索と削除

ディレクトリ内の重複エントリの検索と削除

複数のimgファイルを含むディレクトリがあり、そのうちのいくつかは同じですが、すべての名前が異なります。重複エントリを削除する必要がありますが、外部ツールはなくbashスクリプトのみがあります。私はLinux初心者です。入れ子になったforループを使用して合計を比較し、結果に従って削除してみましたが、md5構文に問題があるため動作しません。助けが必要ですか?

私が試した結果は...

for i in directory_path; do
    sum1='find $i -type f -iname "*.jpg" -exec md5sum '{}' \;'
    for j in directory_path; do
        sum2='find $j -type f -iname "*.jpg" -exec md5sum '{}' \;'
        if test $sum1=$sum2 ; then rm $j ; fi
    done
done

私は得る:test: too many arguments

ベストアンサー1

スクリプトには多くの問題があります。

  • まず、割り当てのために結果コマンドを変数に渡すときは、逆引用符()で囲む必要があります。`command`または、単一引用符()で$(command)囲む必要があります。'command'その後、コマンドの結果を変数に代入するのではなく、コマンド自体がオフになります。したがって、あなたの実際testの内容は次のようになります。

    $ echo "test $sum1=$sum2"
    test find $i -type f -iname "*.jpg" -exec md5sum {} \;=find $j -type f -iname "*.jpg" -exec md5sum {} \;
    
  • 次の問題は、コマンドがmd5sum単純なハッシュ以上を返すことです。

    $ md5sum /etc/fstab
    46f065563c9e88143fa6fb4d3e42a252  /etc/fstab
    

    最初のフィールドのみを比較したいので、最初のフィールドのみをmd5sum印刷するコマンドを渡して出力を解析する必要があります。

    find $i -type f -iname "*.png" -exec md5sum '{}' \; | cut -f 1 -d ' '
    

    または

    find $i -type f -iname "*.png" -exec md5sum '{}' \; | awk '{print $1}' 
    
  • また、このfindコマンドは1つではなく多数の一致を返し、各一致は2番目の一致で繰り返されますfind。これは、ある時点で同じファイルを自分と比較すると、md5sumが同じで最終的に削除されることを意味します。みんなa.jpgあなたのファイル(およびを含むテストディレクトリでこれを実行しましたb.jpg):

    for i in $(find . -iname "*.jpg"); do
      for j in $(find . -iname "*.jpg"); do
         echo "i is: $i and j is: $j"
      done
    done   
    i is: ./a.jpg and j is: ./a.jpg   ## BAD, will delete a.jpg
    i is: ./a.jpg and j is: ./b.jpg
    i is: ./b.jpg and j is: ./a.jpg
    i is: ./b.jpg and j is: ./b.jpg   ## BAD will delete b.jpg
    
  • for i in directory_pathディレクトリ配列を渡さないと実行したくありません。これらのファイルがすべて同じディレクトリにある場合は、for i in $(find directory_path -iname "*.jpg")を実行してすべてを繰り返す必要があります。

  • これは悪い考えforfind 出力にループを使用します。whileループを使用するかワイルドカード:

    find . -iname "*.jpg" | while read i; do [...] ; done
    

    または、すべてのファイルが同じディレクトリにある場合:

    for i in *jpg; do [...]; done
    

    シェルと設定したオプションによっては、サブディレクトリのファイルにワイルドカードを使用することもできますが、ここではこれについては説明しません。

  • 最後に、変数も引用する必要があります。そうしないと、スペースを含むディレクトリパスがスクリプトを中断します。

ファイル名にはスペース、改行、バックスラッシュ、その他の奇妙な文字を含めることができ、ループ内でこれらの文字を正しく処理するにはwhileより多くのオプションを追加する必要があります。書きたい内容は次のとおりです。

find dir_path -type f -iname "*.jpg" -print0 | while IFS= read -r -d '' i; do
  find dir_path -type f -iname "*.jpg" -print0 | while IFS= read -r -d '' j; do
    if [ "$i" != "$j" ]
    then
      sum1=$(md5sum "$i" | cut -f 1 -d ' ' )
      sum2=$(md5sum "$j" | cut -f 1 -d ' ' )
      [ "$sum1" = "$sum2" ] && rm "$j"
    fi
  done
done

より簡単な方法は次のとおりです。

find directory_path -name "*.jpg" -exec md5sum '{}' + | 
 perl -ane '$k{$F[0]}++; system("rm $F[1]") if $k{$F[0]}>1'

ファイル名のスペースを処理できるより良いバージョン:

find directory_path -name "*.jpg" -exec md5sum '{}' + | 
 perl -ane '$k{$F[0]}++; system("rm \"@F[1 .. $#F]\"") if $k{$F[0]}>1'

findこの小さなPerlスクリプトは、コマンドの結果(つまりmd5sumとファイル名)を実行します。オプションは、md5sumやファイル名と同様に、入力行をスペースに分割して-a配列に保存します。 md5sumがハッシュに保存されると、スクリプトはハッシュが表示されていることを確認し()、その場合はファイルを削除します()。perlF$F[0]$F[1]kif $k{$F[0]}>1system("rm $F[1]")


これが機能している間、大規模な画像コレクションの場合、非常に遅くなり、保存するファイルを選択できません。以下を含む、よりエレガントな方法でこの問題を処理する多くのプログラムがあります。

  • fdupes
  • fslint
  • 他のさまざまなオプションがリストされていますここ

おすすめ記事