สคริปต์ของคุณมีปัญหาเล็กน้อย
ครั้งแรกในเพื่อที่จะกำหนดผลของคำสั่งไปยังตัวแปรที่คุณต้องใส่มันทั้งใน backtics ( `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คำสั่งจะกลับมาตรงกับที่หลาย ๆ 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ลูปกับผลลัพธ์ของการค้นหา คุณควรใช้whileลูปหรือกลม :
find . -iname "*.jpg" | while read i; do [...] ; done
หรือหากไฟล์ทั้งหมดของคุณอยู่ในไดเรกทอรีเดียวกัน:
for i in *jpg; do [...]; done
คุณสามารถใช้ globbing แม้กระทั่งไฟล์ในไดเรกทอรีย่อยทั้งนี้ขึ้นอยู่กับเชลล์และตัวเลือกที่คุณตั้งไว้ แต่อย่าเข้าไปดูที่นี่
ท้ายสุดคุณควรอ้างอิงตัวแปรของคุณเส้นทางอื่นที่มีช่องว่างจะทำให้สคริปต์ของคุณแตก
ชื่อไฟล์สามารถมีช่องว่างบรรทัดใหม่แบ็กสแลชและอักขระแปลก ๆ เพื่อจัดการกับไฟล์เหล่านั้นอย่างถูกต้องใน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'
สคริปต์ Perl นี้เล็กน้อยจะทำงานผ่านผลลัพธ์ของfindคำสั่ง (เช่น md5sum และชื่อไฟล์) -aเลือกสำหรับperlสายแยกการป้อนข้อมูลในช่องว่างและบันทึกไว้ในFอาร์เรย์ดังนั้น$F[0]จะเป็น md5sum และ$F[1]ชื่อไฟล์ md5sum จะถูกบันทึกในแฮชkและสคริปต์จะตรวจสอบว่าแฮชถูกเห็นแล้ว ( if $k{$F[0]}>1) และลบไฟล์หากมี ( system("rm $F[1]"))
ในขณะที่ใช้งานได้จะช้ามากสำหรับคอลเลกชันรูปภาพขนาดใหญ่และคุณไม่สามารถเลือกไฟล์ที่จะเก็บได้ มีหลายโปรแกรมที่จัดการสิ่งนี้ในลักษณะที่สง่างามกว่า ได้แก่ :