rozróżnianie wielkości liter w zduplikowanych nazwach plików

17

Czy mogę znaleźć wszystkie pliki w katalogu ze zduplikowanymi nazwami plików, bez względu na wielkość liter (wielkie i / lub małe)?

lamcro
źródło

Odpowiedzi:

14

Jeśli masz narzędzia GNU (lub przynajmniej zestaw, który może poradzić sobie z liniami zakończonymi zerem), inna odpowiedź ma świetną metodę:

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

Uwaga: dane wyjściowe będą miały łańcuchy zakończone na zero; narzędzie, którego używasz do dalszego przetwarzania, powinno być w stanie sobie z tym poradzić.

W przypadku braku narzędzi, które obsługują wiersze z zerowym zakończeniem lub jeśli chcesz mieć pewność, że Twój kod działa w środowiskach, w których takie narzędzia nie są dostępne, potrzebujesz małego skryptu:

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

Co to za szaleństwo? Zobacz tę odpowiedź, aby uzyskać wyjaśnienie technik zapewniających bezpieczeństwo dla zwariowanych nazw plików.

Shawn J. Goff
źródło
1
Chciałem tylko
napisać
2
Czy naprawdę potrzebujesz -mindepth?
rozcietrzewiacz
Używam Solaris. Czy / usr / bin / znajduje ten, o którym mówisz? Próbowałem go użyć i dałem mi wiele błędów.
lamcro
@lamcro Nie, Solaris nie używa GNU find; Zredagowałem odpowiedź, aby uwzględnić rozwiązanie inne niż GNU.
Shawn J. Goff,
Dobrze. Czy po prostu wklejam go do pliku tekstowego i daję mu uprawnienia do wykonywania?
lamcro 18.10.11
12

Istnieje wiele skomplikowanych odpowiedzi powyżej, wydaje się to prostsze i szybsze niż wszystkie:

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

Jeśli chcesz znaleźć zduplikowane nazwy plików w podkatalogach, musisz porównać tylko nazwę pliku, a nie całą ścieżkę:

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

Edycja: Shawn J. Goff wskazał, że to się nie powiedzie, jeśli masz nazwy plików ze znakami nowej linii. Jeśli używasz narzędzi GNU, możesz też sprawić, by działały:

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

Opcja -print0(dla find) i -zopcja (dla sort i uniq) powoduje, że działają one na ciągach zakończonych znakiem NUL, zamiast ciągów zakończonych znakiem nowej linii. Ponieważ nazwy plików nie mogą zawierać wartości NUL, działa to dla wszystkich nazw plików.

Jamie Kitson
źródło
1
Ale zobacz mój komentarz do odpowiedzi Shawna J. Goffa, możesz dodać opcję -print0, aby znaleźć, oraz opcję -z, aby uniq i sortować. Również chcesz -f na sort. To działa. (Zamierzam zmienić to w twoją odpowiedź, możesz cofnąć, jeśli nie zatwierdzisz)
derobert
Ostatnie polecenie daje mi wynik bez powrotu karetki (wynik jest w jednym wierszu). Korzystam z systemu Red Hat Linux, aby uruchomić polecenie. Pierwsza linia poleceń działa najlepiej dla mnie.
niedz.
2

Posortuj listę nazw plików bez rozróżniania wielkości liter i wydrukuj duplikaty. sortma opcję sortowania bez rozróżniania wielkości liter. Podobnie GNU uniq, ale nie inne implementacje, a wszystko, co możesz zrobić, uniqto wydrukować każdy element w zestawie duplikatów oprócz pierwszego napotkanego. Dzięki narzędziom GNU, zakładając, że żadna nazwa pliku nie zawiera nowej linii, istnieje łatwy sposób wydrukowania wszystkich elementów oprócz jednego w każdym zestawie duplikatów:

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

Przenośnie, aby wydrukować wszystkie elementy w każdym zestawie duplikatów, przy założeniu, że żadna nazwa pliku nie zawiera nowego wiersza:

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

Jeśli potrzebujesz dostosować nazwy plików zawierające nowe wiersze, wybierz Perla lub Pythona. Należy pamiętać, że może być konieczne dostosowanie danych wyjściowych lub lepsze przetwarzanie w tym samym języku, ponieważ poniższy przykładowy kod używa znaków nowej linii do oddzielania nazw we własnych wynikach.

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

Oto czyste rozwiązanie Zsh. Jest to trochę szczegółowe, ponieważ nie ma wbudowanego sposobu na zachowanie zduplikowanych elementów w tablicy lub wyniku globalnym.

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
Gilles „SO- przestań być zły”
źródło
1

Bez GNU find:

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

Rudolf Adamkovic
źródło
2
trbardzo prawdopodobne jest, że spowoduje spustoszenie w każdym zestawie znaków, który wykorzystuje więcej niż jeden bajt na znak. Tylko pierwsze 256 znaków UTF-8 jest bezpiecznych podczas używania tr. Z Wikipedii tr (Unix) .. Większość wersji tr, w tym GNU tri klasyczny Unix tr, działa na SINGLE BYTES i nie jest zgodna z Unicode ..
Peter.O
1
Zaktualizuj mój poprzedni komentarz. Tylko pierwszych 128 znaków UTF-8 jest bezpiecznych. Wszystkie znaki UTF-8 powyżej porządkowego zakresu 0..127 są wielobajtowe i mogą mieć indywidualne wartości bajtów w innych znakach. Tylko bajty z zakresu 0..127 mają powiązanie jeden-do-jednego z unikalnym znakiem.
Peter.O
Plus uniqma flagę bez rozróżniania wielkości liter i.
Jamie Kitson
1

W końcu udało mi się to w ten sposób:

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

Użyłem findzamiast lsdlatego, że potrzebowałem pełnej ścieżki (wiele podkatalogów). Nie znalazłem, jak to zrobić ls.

lamcro
źródło
2
Zarówno sorti uniqmają ignorować przypadków flagi, F i I odpowiednio.
Jamie Kitson,
-1

Dla każdego, kto chce następnie zmienić nazwę itp. Jednego z plików:

find . -maxdepth 1 | sort -f | uniq -di | while read f; do echo mv "$f" "${f/.txt/_.txt}"; done
użytkownik3342930
źródło