複数の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"
)を実行してすべてを繰り返す必要があります。これは悪い考え
for
find 出力にループを使用します。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がハッシュに保存されると、スクリプトはハッシュが表示されていることを確認し()、その場合はファイルを削除します()。perl
F
$F[0]
$F[1]
k
if $k{$F[0]}>1
system("rm $F[1]")
これが機能している間、大規模な画像コレクションの場合、非常に遅くなり、保存するファイルを選択できません。以下を含む、よりエレガントな方法でこの問題を処理する多くのプログラムがあります。
答え2
fdupes
プロセス全体を簡素化し、ユーザーに重複エントリを削除するように求める素晴らしいプログラムがあります。確認してみる価値があると思います。
$ fdupes --delete DIRECTORY_WITH_DUPLICATES
[1] DIRECTORY_WITH_DUPLICATES/package-0.1-linux.tar.gz
[2] DIRECTORY_WITH_DUPLICATES/package-0.1-linux.tar.gz.1
Set 1 of 1, preserve files [1 - 2, all]: 1
[+] DIRECTORY_WITH_DUPLICATES/package-0.1-linux.tar.gz
[-] DIRECTORY_WITH_DUPLICATES/package-0.1-linux.tar.gz.1
デフォルトでは、どのファイルを選択するかを尋ねられます。維持する、私は入った1、2番目のアイテムを削除しました。
他の興味深いオプションは次のとおりです。
-r --recurse
for every directory given follow subdirectories encountered within
-N --noprompt
when used together with --delete, preserve the first file in each set of duplicates and delete the others without prompting the user
あなたの例では、次のように実行できます。
fdupes --recurse --delete --noprompt DIRECTORY_WITH_DUPLICATES
利用可能なすべてのオプションについては、リソースを参照してくださいman fdupes
。