สคริปต์ของคุณมีปัญหาเล็กน้อย
ครั้งแรกในเพื่อที่จะกำหนดผลของคำสั่งไปยังตัวแปรที่คุณต้องใส่มันทั้งใน 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]")
)
ในขณะที่ใช้งานได้จะช้ามากสำหรับคอลเลกชันรูปภาพขนาดใหญ่และคุณไม่สามารถเลือกไฟล์ที่จะเก็บได้ มีหลายโปรแกรมที่จัดการสิ่งนี้ในลักษณะที่สง่างามกว่า ได้แก่ :