pencarian case-sensitive dari nama file duplikat


Jawaban:


14

Jika Anda memiliki utilitas GNU (atau setidaknya satu set yang dapat menangani baris tanpa-penghentian), jawaban lain memiliki metode yang hebat:

find . -maxdepth 1 -print0 | sort -z | uniq -diz

Catatan: output akan memiliki string yang diakhiri nol; alat yang Anda gunakan untuk proses selanjutnya harus bisa mengatasinya.

Dengan tidak adanya alat yang berurusan dengan garis nol-putus, atau jika Anda ingin memastikan kode Anda bekerja di lingkungan di mana alat tersebut tidak tersedia, Anda memerlukan skrip kecil:

#!/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

Gila apa ini? Lihat jawaban ini untuk penjelasan tentang teknik yang membuat ini aman untuk nama file gila.


1
Saya hanya akan memposting jawaban yang serupa ... Tapi lebih buruk :)
rozcietrzewiacz

2
Apakah Anda benar-benar membutuhkannya -mindepth?
rozcietrzewiacz

Saya menggunakan Solaris. Apakah / usr / bin / temukan yang Anda bicarakan? Saya mencoba menggunakannya dan memberi saya banyak kesalahan.
lamcro

@lamcro Tidak, Solaris tidak menggunakan GNU find; Saya telah mengedit jawaban untuk menyertakan solusi non-GNU.
Shawn J. Goff

Baik. Apakah saya hanya menempelkannya di file teks dan memberikannya hak eksekusi?
lamcro

12

Ada banyak jawaban rumit di atas, ini tampaknya lebih sederhana dan lebih cepat daripada semuanya:

find . -maxdepth 1 | sort -f | uniq -di

Jika Anda ingin menemukan nama file duplikat di subdirektori maka Anda hanya perlu membandingkan nama file, bukan keseluruhan path:

find . -maxdepth 2 -printf "%f\n" | sort -f | uniq -di

Sunting: Shawn J. Goff telah menunjukkan bahwa ini akan gagal jika Anda memiliki nama file dengan karakter baris baru. Jika Anda menggunakan utilitas GNU, Anda dapat membuatnya bekerja juga:

find . -maxdepth 1 -print0 | sort -fz | uniq -diz

Opsi -print0(untuk menemukan) dan -z(untuk mengurutkan dan uniq) menyebabkan mereka bekerja pada string yang diakhiri NUL, alih-alih string yang diakhiri baris baru. Karena nama file tidak dapat berisi NUL, ini berfungsi untuk semua nama file.


1
Tetapi lihat komentar saya pada jawaban Shawn J. Goff, Anda dapat menambahkan opsi -print0 untuk menemukan, dan opsi -z ke uniq dan mengurutkan. Juga, Anda ingin -f diurutkan juga. Lalu berhasil. (Saya akan mengedit ini menjadi jawaban Anda, silakan kembali jika Anda tidak menyetujui)
derobert

Perintah terakhir memberi saya output tanpa carriage return (hasilnya semua dalam satu baris). Saya menggunakan Red Hat Linux untuk menjalankan perintah. Baris perintah pertama bekerja paling baik untuk saya.
Minggu

2

Urutkan daftar nama file dengan cara case-sensitive dan cetak duplikat. sortmemiliki opsi untuk penyortiran case-insensitive. Begitu juga GNU uniq, tetapi bukan implementasi lain, dan yang dapat Anda lakukan uniqhanyalah mencetak setiap elemen dalam satu set duplikat kecuali yang pertama kali ditemukan. Dengan alat GNU, dengan asumsi bahwa tidak ada nama file berisi baris baru, ada cara mudah untuk mencetak semua elemen kecuali satu di setiap set duplikat:

for x in *; do printf "%s\n" "$x"; done |
sort -f |
uniq -id

Portable, untuk mencetak semua elemen dalam setiap set duplikat, dengan asumsi bahwa tidak ada nama file yang mengandung baris baru:

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 }'

Jika Anda perlu mengakomodasi nama file yang berisi baris baru, pilih Perl atau Python. Perhatikan bahwa Anda mungkin perlu mengubah output, atau lebih baik melakukan pemrosesan lebih lanjut dalam bahasa yang sama, karena kode sampel di bawah ini menggunakan baris baru untuk memisahkan nama dalam outputnya sendiri.

perl -e '
    foreach (glob("*")) {push @{$f{lc($_)}}, $_}
    foreach (keys %f) {@names = @{$f{$_}}; if (@names > 1) {print "$_\n" foreach @names}}
'

Inilah solusi zsh murni. Ini agak bertele-tele, karena tidak ada cara bawaan untuk menyimpan elemen duplikat dalam array atau hasil glob.

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

Tanpa GNU find:

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


2
tradalah sangat mungkin untuk melampiaskan malapetaka di setiap set karakter yang menggunakan lebih dari satu byte per karakter. Hanya 256 karakter pertama dari UTF-8 yang aman saat digunakan tr. Dari Wikipedia tr (Unix) .. Sebagian besar versi tr, termasuk GNU trdan Unix klasik tr, beroperasi pada SINGLE BYTES dan tidak sesuai dengan Unicode ..
Peter.O

1
Perbarui komentar saya sebelumnya .. hanya 128 karakter pertama dari UTF-8 yang aman. Semua karakter UTF-8 di atas rentang ordinal 0..127 semuanya multi-byte dan dapat memiliki nilai byte individual dalam karakter lain. Hanya byte dalam kisaran 0..127 yang memiliki asosiasi satu-ke-satu dengan karakter unik.
Peter.O

Plus uniqmemiliki flag case-insensitive i.
Jamie Kitson

1

Saya akhirnya berhasil seperti ini:

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

Saya menggunakan findalih-alih lskarena saya membutuhkan path lengkap (banyak subdirektori) yang disertakan. Saya tidak menemukan cara melakukan ini ls.


2
Keduanya sortdan masing-masing uniqmemiliki flag abaikan, f dan i.
Jamie Kitson

-1

Untuk siapa pun yang ingin mengubah nama, dll, salah satu file:

find . -maxdepth 1 | sort -f | uniq -di | while read f; do echo mv "$f" "${f/.txt/_.txt}"; done
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.