Suche nach doppelten Dateinamen ohne Berücksichtigung der Groß- und Kleinschreibung


17

Gibt es eine Möglichkeit, alle Dateien in einem Verzeichnis mit doppelten Dateinamen zu finden, unabhängig von der Schreibweise (Groß- und / oder Kleinschreibung)?

Antworten:


14

Wenn Sie GNU-Dienstprogramme (oder zumindest einen Satz, der mit Zeilen mit Nullterminierung umgehen kann) zur Verfügung haben, bietet eine andere Antwort eine großartige Methode:

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

Hinweis: Die Ausgabe enthält nullterminierte Zeichenfolgen. Das Tool, das Sie für die weitere Verarbeitung verwenden, sollte in der Lage sein, damit umzugehen.

In Ermangelung von Tools, die sich mit Zeilen mit Nullterminierung befassen, oder wenn Sie sicherstellen möchten, dass Ihr Code in Umgebungen funktioniert, in denen solche Tools nicht verfügbar sind, benötigen Sie ein kleines Skript:

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

Was ist das für ein Wahnsinn? In dieser Antwort finden Sie eine Erklärung der Techniken, die dies für verrückte Dateinamen sicher machen.


1
Ich wollte gerade ein ähnliches posten ... Aber schlechtere Antwort :)
rozcietrzewiacz 18.10.11

2
Brauchst du die wirklich -mindepth?
rozcietrzewiacz

Ich benutze Solaris. Ist / usr / bin / derjenige, über den Sie sprechen? Ich habe es ausprobiert und mir viele Fehler gemacht.
Lamcro

@lamcro Nein, Solaris verwendet keine GNUs find. Ich habe die Antwort so bearbeitet, dass sie eine Nicht-GNU-Lösung enthält.
Shawn J. Goff

In Ordnung. Füge ich es einfach in eine Textdatei ein und gebe ihm Ausführungsrechte?
Lamcro

12

Es gibt viele komplizierte Antworten, dies scheint einfacher und schneller zu sein als alle:

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

Wenn Sie doppelte Dateinamen in Unterverzeichnissen finden möchten, müssen Sie nur den Dateinamen und nicht den gesamten Pfad vergleichen:

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

Edit: Shawn J. Goff hat darauf hingewiesen, dass dies fehlschlagen wird, wenn Sie Dateinamen mit Zeilenumbrüchen haben. Wenn Sie GNU-Dienstprogramme verwenden, können Sie diese auch zum Laufen bringen:

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

Die Optionen -print0(for find) und -z(for sort and uniq) bewirken, dass sie mit NUL-terminierten Zeichenfolgen anstatt mit Newline-terminierten Zeichenfolgen arbeiten. Da Dateinamen nicht NUL enthalten dürfen, funktioniert dies für alle Dateinamen.


1
Aber siehe meinen Kommentar zu Shawn J. Goffs Antwort, Sie können die Option -print0 hinzufügen, um zu suchen, und die Option -z, um zu vereinheitlichen und zu sortieren. Außerdem möchten Sie -f auch sortieren. Dann klappt es. (Ich werde dies in Ihre Antwort
ändern.

Der letzte Befehl gibt eine Ausgabe ohne Wagenrücklauf aus (Ergebnis steht in einer Zeile). Ich verwende Red Hat Linux, um den Befehl auszuführen. Die erste Befehlszeile funktioniert am besten für mich.
So,

2

Sortieren Sie die Liste der Dateinamen unabhängig von Groß- und Kleinschreibung und drucken Sie Duplikate. sorthat eine Option für die Sortierung ohne Berücksichtigung der Groß- und Kleinschreibung. Genauso wie GNU uniq, aber keine anderen Implementierungen, und alles, was Sie tun können, uniqist, jedes Element in einer Reihe von Duplikaten zu drucken, mit Ausnahme des ersten, das angetroffen wird. Unter der Annahme, dass kein Dateiname eine neue Zeile enthält, gibt es mit GNU-Werkzeugen eine einfache Möglichkeit, alle Elemente außer einem in jedem Satz von Duplikaten auszudrucken:

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

Portabel, um alle Elemente in jedem Satz von Duplikaten zu drucken, vorausgesetzt, kein Dateiname enthält eine neue Zeile:

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

Wenn Sie Dateinamen mit Zeilenumbrüchen aufnehmen müssen, wählen Sie Perl oder Python. Beachten Sie, dass Sie möglicherweise die Ausgabe optimieren oder die weitere Verarbeitung in derselben Sprache durchführen müssen, da der folgende Beispielcode Zeilenumbrüche verwendet, um Namen in der eigenen Ausgabe zu trennen.

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

Hier ist eine reine zsh-Lösung. Es ist etwas ausführlich, da es keine integrierte Möglichkeit gibt, die doppelten Elemente in einem Array oder Glob-Ergebnis zu speichern.

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

Ohne GNU find:

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


2
trEs ist sehr wahrscheinlich, dass jeder Zeichensatz, der mehr als ein einziges Byte pro Zeichen verwendet, Schaden anrichtet. Nur die ersten 256 Zeichen von UTF-8 sind bei der Verwendung sicher tr. Aus Wikipedia tr (Unix) . Die meisten Versionen von tr, einschließlich GNU trund klassischem Unix tr, arbeiten mit SINGLE BYTES und sind nicht Unicode-kompatibel.
Peter.O

1
Aktualisieren Sie auf meinen vorherigen Kommentar. Nur die ersten 128 Zeichen von UTF-8 sind sicher. Alle UTF-8-Zeichen oberhalb des Ordnungsbereichs 0..127 bestehen aus mehreren Bytes und können einzelne Bytewerte in anderen Zeichen enthalten. Nur die Bytes im Bereich 0..127 weisen eine Eins-zu-Eins-Zuordnung zu einem eindeutigen Zeichen auf.
Peter.O

Plus uniqhat ein Flag i, das die Groß- und Kleinschreibung nicht berücksichtigt.
Jamie Kitson

1

Ich habe es endlich so geschafft:

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

Ich habe findanstelle von lsweil ich brauchte den vollständigen Pfad (viele Unterverzeichnisse) enthalten. Ich habe nicht gefunden, wie ich das machen soll ls.


2
Beide sortund uniqhaben jeweils die Groß- und Kleinschreibung f und i ignoriert.
Jamie Kitson

-1

Für alle anderen, die dann eine der Dateien usw. umbenennen möchten:

find . -maxdepth 1 | sort -f | uniq -di | while read f; do echo mv "$f" "${f/.txt/_.txt}"; done
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.