ฉันมีวิธีการค้นหาไฟล์ทั้งหมดในไดเรกทอรีที่มีชื่อไฟล์ซ้ำกันหรือไม่โดยไม่คำนึงถึงตัวอักษร (ตัวพิมพ์ใหญ่และ / หรือตัวพิมพ์เล็ก)
ฉันมีวิธีการค้นหาไฟล์ทั้งหมดในไดเรกทอรีที่มีชื่อไฟล์ซ้ำกันหรือไม่โดยไม่คำนึงถึงตัวอักษร (ตัวพิมพ์ใหญ่และ / หรือตัวพิมพ์เล็ก)
คำตอบ:
หากคุณมีโปรแกรมอรรถประโยชน์ของ GNU (หรืออย่างน้อยชุดที่สามารถจัดการกับบรรทัดที่สิ้นสุดด้วยศูนย์) คำตอบอื่นมีวิธีที่ยอดเยี่ยม:
find . -maxdepth 1 -print0 | sort -z | uniq -diz
หมายเหตุ: ผลลัพธ์จะมีสตริงที่สิ้นสุดด้วยศูนย์; เครื่องมือที่คุณใช้ในการดำเนินการเพิ่มเติมควรสามารถจัดการได้
ในกรณีที่ไม่มีเครื่องมือที่จัดการกับบรรทัดที่ไม่มีการสิ้นสุดหรือหากคุณต้องการตรวจสอบให้แน่ใจว่าโค้ดของคุณทำงานในสภาพแวดล้อมที่ไม่มีเครื่องมือดังกล่าวคุณต้องมีสคริปต์ขนาดเล็ก:
#!/bin/sh
for f in *; do
find . -maxdepth 1 -iname ./"$f" -exec echo \; | wc -l | while read count; do
[ $count -gt 1 ] && echo $f
done
done
ความบ้าคลั่งนี้คืออะไร? ดูคำตอบนี้สำหรับคำอธิบายเกี่ยวกับเทคนิคต่าง ๆ ที่ทำให้ชื่อไฟล์นี้ปลอดภัย
-mindepth
หรือ
find
; ฉันได้แก้ไขคำตอบเพื่อรวมโซลูชันที่ไม่ใช่ของ GNU
มีคำตอบที่ซับซ้อนหลายข้อข้างต้นดูเหมือนง่ายกว่าและเร็วกว่าทั้งหมด:
find . -maxdepth 1 | sort -f | uniq -di
หากคุณต้องการค้นหาชื่อไฟล์ที่ซ้ำกันในไดเรกทอรีย่อยคุณต้องเปรียบเทียบเฉพาะชื่อไฟล์ไม่ใช่เส้นทางทั้งหมด:
find . -maxdepth 2 -printf "%f\n" | sort -f | uniq -di
แก้ไข: Shawn J. Goff ได้ชี้ให้เห็นว่าสิ่งนี้จะล้มเหลวหากคุณมีชื่อไฟล์ที่มีอักขระขึ้นบรรทัดใหม่ หากคุณใช้ยูทิลิตี้ GNU คุณก็สามารถทำงานเหล่านี้ได้เช่นกัน:
find . -maxdepth 1 -print0 | sort -fz | uniq -diz
กระบวนการ-print0
(เพื่อค้นหา) และ-z
ตัวเลือก (สำหรับการเรียงลำดับและ uniq) ทำให้สตริงเหล่านี้ทำงานบนสตริงที่ถูกยกเลิกด้วย NUL แทนที่จะเป็นสตริงที่ยกเลิกการขึ้นบรรทัดใหม่ เนื่องจากชื่อไฟล์ต้องไม่มี NUL จึงสามารถใช้งานได้กับชื่อไฟล์ทั้งหมด
เรียงลำดับรายชื่อไฟล์ในแบบตัวพิมพ์เล็กและตัวพิมพ์ sort
มีตัวเลือกสำหรับการเรียงลำดับแบบคำนึงถึงขนาดตัวพิมพ์ GNU ก็uniq
เช่นกัน แต่ไม่ใช่การนำไปใช้งานอื่น ๆ และสิ่งที่คุณทำได้uniq
คือพิมพ์องค์ประกอบทุกอย่างในกลุ่มของรายการที่ซ้ำกันยกเว้นรายการแรกที่พบ ด้วยเครื่องมือของ GNU สมมติว่าไม่มีชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่มีวิธีที่ง่ายในการพิมพ์องค์ประกอบทั้งหมด แต่จะมีหนึ่งรายการในแต่ละชุดที่ซ้ำกัน:
for x in *; do printf "%s\n" "$x"; done |
sort -f |
uniq -id
เพื่อพิมพ์องค์ประกอบทั้งหมดในรายการที่ซ้ำกันแต่ละชุดสมมติว่าไม่มีชื่อไฟล์ใดที่มีบรรทัดใหม่:
for x in *; do printf "%s\n" "$x"; done |
sort -f |
awk '
tolower($0) == tolower(prev) {
print prev;
while (tolower($0) == tolower(prev)) {print; getline}
}
1 { prev = $0 }'
หากคุณต้องการรองรับชื่อไฟล์ที่มีบรรทัดใหม่ให้ไปหา Perl หรือ Python โปรดทราบว่าคุณอาจต้องปรับแต่งเอาต์พุตหรือทำการประมวลผลเพิ่มเติมในภาษาเดียวกันให้ดีขึ้นเนื่องจากโค้ดตัวอย่างด้านล่างใช้บรรทัดใหม่เพื่อแยกชื่อในเอาต์พุตของตัวเอง
perl -e '
foreach (glob("*")) {push @{$f{lc($_)}}, $_}
foreach (keys %f) {@names = @{$f{$_}}; if (@names > 1) {print "$_\n" foreach @names}}
'
นี่เป็นวิธีแก้ปัญหา zsh แท้ๆ มันค่อนข้างละเอียดเนื่องจากไม่มีวิธีที่จะทำให้องค์ประกอบที่ซ้ำกันอยู่ในอาร์เรย์หรือผลลัพธ์กลม
a=(*)(N); a=("${(@io)a}")
[[ $#a -le 1 ]] ||
for i in {2..$#a}; do
if [[ ${(L)a[$i]} == ${(L)a[$((i-1))]} ]]; then
[[ ${(L)a[$i-2]} == ${(L)a[$((i-1))]} ]] || print -r $a[$((i-1))]
print -r $a[$i]
fi
done
ไม่มี GNU find
:
LANG=en_US ls | tr '[A-Z]' '[a-z]' | uniq -c | awk '$1 >= 2 {print $2}'
tr
มีความเป็นไปได้สูงที่จะเกิดความเสียหายกับชุดอักขระใด ๆ ที่ใช้มากกว่าหนึ่งไบต์ต่อตัวอักษร เพียง 256 ตัวอักษรแรกของ UTF-8 tr
มีความปลอดภัยเมื่อใช้ จากWikipedia tr (Unix) .. เวอร์ชั่นส่วนใหญ่tr
รวมถึง GNU tr
และ Unix แบบคลาสสิกtr
ทำงานบน SINGLE BYTES และไม่ใช่ Unicode ที่เป็นไปตามมาตรฐาน ..
uniq
มีการกำหนดธงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่
ในที่สุดฉันก็จัดการแบบนี้:
find . | tr '[:upper:]' '[:lower:]' | sort | uniq -d
ฉันใช้find
แทนls
สาเหตุที่ฉันต้องการเส้นทางแบบเต็ม (ไดเรกทอรีย่อยจำนวนมาก) รวมอยู่ด้วย ls
ฉันไม่พบวิธีการที่จะทำเช่นนี้กับ
sort
และuniq
มีค่าแฟล็กตัวพิมพ์เล็ก f และ i ตามลำดับ
สำหรับคนอื่น ๆ ที่ต้องการเปลี่ยนชื่อไฟล์ ฯลฯ เป็นไฟล์ใดไฟล์หนึ่ง:
find . -maxdepth 1 | sort -f | uniq -di | while read f; do echo mv "$f" "${f/.txt/_.txt}"; done