การค้นหาแบบคำนึงถึงขนาดตัวพิมพ์ของชื่อไฟล์ซ้ำกัน


17

ฉันมีวิธีการค้นหาไฟล์ทั้งหมดในไดเรกทอรีที่มีชื่อไฟล์ซ้ำกันหรือไม่โดยไม่คำนึงถึงตัวอักษร (ตัวพิมพ์ใหญ่และ / หรือตัวพิมพ์เล็ก)

คำตอบ:


14

หากคุณมีโปรแกรมอรรถประโยชน์ของ 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

ความบ้าคลั่งนี้คืออะไร? ดูคำตอบนี้สำหรับคำอธิบายเกี่ยวกับเทคนิคต่าง ๆ ที่ทำให้ชื่อไฟล์นี้ปลอดภัย


1
ผมก็จะโพสต์ที่คล้ายกัน ... แต่ที่เลวร้ายยิ่งคำตอบ :)
rozcietrzewiacz

2
คุณต้องการจริงๆ-mindepthหรือ
rozcietrzewiacz

ฉันใช้โซลาริส / usr / bin / ค้นหาสิ่งที่คุณกำลังพูดถึงอยู่หรือไม่? ฉันลองใช้มันและทำให้ฉันมีข้อผิดพลาดมากมาย
lamcro

@lamcro ไม่ Solaris ไม่ใช้ GNU's find; ฉันได้แก้ไขคำตอบเพื่อรวมโซลูชันที่ไม่ใช่ของ GNU
Shawn J. Goff

ตกลง. ฉันเพิ่งวางลงในไฟล์ข้อความและให้สิทธิ์ในการดำเนินการหรือไม่
lamcro

12

มีคำตอบที่ซับซ้อนหลายข้อข้างต้นดูเหมือนง่ายกว่าและเร็วกว่าทั้งหมด:

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 จึงสามารถใช้งานได้กับชื่อไฟล์ทั้งหมด


1
แต่ดูความคิดเห็นของฉันเกี่ยวกับคำตอบของ Shawn J. Goff คุณสามารถเพิ่มตัวเลือก -print0 เพื่อค้นหาและตัวเลือก -z เพื่อ uniq และเรียงลำดับ นอกจากนี้คุณต้องการ -f ในการจัดเรียงเช่นกัน จากนั้นก็ใช้งานได้ (ฉันจะแก้ไขนี้ลงในคำตอบของคุณรู้สึกอิสระที่จะย้อนกลับไปถ้าคุณไม่อนุมัติ)
derobert

คำสั่งสุดท้ายคือการให้ฉันเอาท์พุทโดยไม่ต้องกลับรถ (ผลที่ได้คือทั้งหมดในหนึ่งบรรทัด) ฉันใช้ Red Hat Linux เพื่อรันคำสั่ง บรรทัดคำสั่งแรกทำงานได้ดีที่สุดสำหรับฉัน
อาทิตย์

2

เรียงลำดับรายชื่อไฟล์ในแบบตัวพิมพ์เล็กและตัวพิมพ์ 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

1

ไม่มี GNU find:

LANG=en_US ls | tr '[A-Z]' '[a-z]' | uniq -c | awk '$1 >= 2 {print $2}'


2
trมีความเป็นไปได้สูงที่จะเกิดความเสียหายกับชุดอักขระใด ๆ ที่ใช้มากกว่าหนึ่งไบต์ต่อตัวอักษร เพียง 256 ตัวอักษรแรกของ UTF-8 trมีความปลอดภัยเมื่อใช้ จากWikipedia tr (Unix) .. เวอร์ชั่นส่วนใหญ่trรวมถึง GNU trและ Unix แบบคลาสสิกtrทำงานบน SINGLE BYTES และไม่ใช่ Unicode ที่เป็นไปตามมาตรฐาน ..
Peter.O

1
อัปเดตเป็นความคิดเห็นก่อนหน้าของฉัน .. เพียง128 ข้อแรกอักขระตัวของ UTF-8 เท่านั้นที่ปลอดภัย อักขระ UTF-8 ทั้งหมดที่อยู่เหนือช่วงลำดับ 0..127เป็นแบบมัลติไบต์ทั้งหมดและสามารถมีค่าไบต์เดี่ยวในอักขระอื่น ๆ เฉพาะไบต์ในช่วง0..127 เท่านั้นที่ มีความสัมพันธ์แบบหนึ่งต่อหนึ่งกับอักขระที่ไม่ซ้ำกัน
Peter.O

Plus uniqมีการกำหนดธงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่
เจมี่คิทสัน

1

ในที่สุดฉันก็จัดการแบบนี้:

find . | tr '[:upper:]' '[:lower:]' | sort | uniq -d

ฉันใช้findแทนlsสาเหตุที่ฉันต้องการเส้นทางแบบเต็ม (ไดเรกทอรีย่อยจำนวนมาก) รวมอยู่ด้วย lsฉันไม่พบวิธีการที่จะทำเช่นนี้กับ


2
ทั้งสองsortและuniqมีค่าแฟล็กตัวพิมพ์เล็ก f และ i ตามลำดับ
Jamie Kitson

-1

สำหรับคนอื่น ๆ ที่ต้องการเปลี่ยนชื่อไฟล์ ฯลฯ เป็นไฟล์ใดไฟล์หนึ่ง:

find . -maxdepth 1 | sort -f | uniq -di | while read f; do echo mv "$f" "${f/.txt/_.txt}"; done
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.