Ada beberapa masalah dalam skrip Anda.
Pertama, untuk menetapkan hasil perintah ke variabel, Anda harus menyertakannya dalam backtics ( `command`
) atau, lebih disukai $(command)
,. Anda memilikinya dalam tanda kutip tunggal ( 'command'
) yang alih-alih menetapkan hasil perintah Anda ke variabel Anda, tetapkan perintah itu sendiri sebagai string. Karena itu, test
sebenarnya Anda:
$ echo "test $sum1=$sum2"
test find $i -type f -iname "*.jpg" -exec md5sum {} \;=find $j -type f -iname "*.jpg" -exec md5sum {} \;
Masalah berikutnya adalah bahwa perintah md5sum
mengembalikan lebih dari sekedar hash:
$ md5sum /etc/fstab
46f065563c9e88143fa6fb4d3e42a252 /etc/fstab
Anda hanya ingin membandingkan bidang pertama, jadi Anda harus mem-parsing md5sum
keluaran dengan meneruskannya melalui perintah yang hanya mencetak bidang pertama:
find $i -type f -iname "*.png" -exec md5sum '{}' \; | cut -f 1 -d ' '
atau
find $i -type f -iname "*.png" -exec md5sum '{}' \; | awk '{print $1}'
Juga, find
perintah akan mengembalikan banyak kecocokan, tidak hanya satu dan masing-masing kecocokan akan digandakan oleh yang kedua find
. Ini berarti bahwa pada titik tertentu Anda akan membandingkan file yang sama dengan dirinya sendiri, md5sum akan identik dan Anda akhirnya akan menghapus semua file Anda (saya menjalankan ini pada test dir yang berisi a.jpg
dan 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
Anda tidak ingin menjalankan for i in directory_path
kecuali Anda melewati array direktori. Jika semua file ini berada di direktori yang sama, Anda ingin menjalankan for i in $(find directory_path -iname "*.jpg"
) untuk menelusuri semua file.
Adalah ide yang buruk untuk menggunakan for
loop dengan output dari find. Anda harus menggunakan while
loop atau globbing :
find . -iname "*.jpg" | while read i; do [...] ; done
atau, jika semua file Anda berada di direktori yang sama:
for i in *jpg; do [...]; done
Bergantung pada shell Anda dan opsi yang telah Anda tentukan, Anda dapat menggunakan globbing bahkan untuk file dalam subdirektori, tetapi jangan membahasnya di sini.
Akhirnya, Anda juga harus mengutip variabel Anda jalur direktori lain dengan spasi akan merusak skrip Anda.
Nama file dapat berisi spasi, baris baru, garis miring terbalik dan karakter aneh lainnya, untuk mengatasinya dengan benar dalam satu while
lingkaran, Anda harus menambahkan beberapa opsi lagi. Yang ingin Anda tulis adalah sesuatu seperti:
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
Cara yang lebih sederhana adalah:
find directory_path -name "*.jpg" -exec md5sum '{}' + |
perl -ane '$k{$F[0]}++; system("rm $F[1]") if $k{$F[0]}>1'
Versi yang lebih baik yang dapat menangani spasi dalam nama file:
find directory_path -name "*.jpg" -exec md5sum '{}' + |
perl -ane '$k{$F[0]}++; system("rm \"@F[1 .. $#F]\"") if $k{$F[0]}>1'
Script Perl kecil ini akan dijalankan melalui hasil dari find
perintah (yaitu md5sum dan nama file). The -a
pilihan untuk perl
lini perpecahan masukan pada spasi dan menyimpannya dalam F
array, sehingga $F[0]
akan menjadi md5sum dan $F[1]
nama file. Md5sum disimpan dalam hash k
dan skrip memeriksa apakah hash sudah terlihat ( if $k{$F[0]}>1
) dan menghapus file jika hash ( system("rm $F[1]")
).
Meskipun itu akan berhasil, akan sangat lambat untuk koleksi gambar besar dan Anda tidak dapat memilih file mana yang akan disimpan. Ada banyak program yang menangani ini dengan cara yang lebih elegan termasuk: