it-swarm.com.de

Wie finde ich doppelte Dateien mit demselben Namen, aber in verschiedenen Fällen, die in Linux in demselben Verzeichnis existieren?

Wie kann ich eine Liste von Dateien mit dem Namen duplicates zurückgeben, d. H. Mit demselben Namen, aber in verschiedenen Fällen, die im Verzeichnis same vorhanden sind?

Der Inhalt der Dateien ist mir egal. Ich muss nur den Speicherort und den Namen aller Dateien kennen, die ein Duplikat mit demselben Namen haben.

Beispielduplikate:

/www/images/taxi.jpg
/www/images/Taxi.jpg

Im Idealfall muss ich alle Dateien rekursiv in einem Basisverzeichnis durchsuchen. Im obigen Beispiel war es /www/

28
Camsoft

Die andere Antwort ist großartig, aber anstelle des "ziemlich monströsen" Perl-Skripts schlage ich vor

Perl -pe 's!([^/]+)$!lc $1!e'

Dadurch wird nur der Dateiname des Pfads in Kleinbuchstaben geschrieben.

Edit 1: Tatsächlich kann das gesamte Problem gelöst werden mit:

find . | Perl -ne 's!([^/]+)$!lc $1!e; print if 1 == $seen{$_}++'

Edit 3: Ich habe mit sed, sort und uniq eine Lösung gefunden, die auch die Duplikate ausdrucken wird, aber sie funktioniert nur, wenn in den Dateinamen keine Leerzeichen vorhanden sind:

find . |sed 's,\(.*\)/\(.*\)$,\1/\2\t\1/\L\2,'|sort|uniq -D -f 1|cut -f 1

Edit 2: Und hier ist ein längeres Skript, das die Namen ausdrucken wird, es benötigt eine Liste von Pfaden auf stdin, wie von find angegeben. Nicht so elegant, aber trotzdem:

#!/usr/bin/Perl -w

use strict;
use warnings;

my %dup_series_per_dir;
while (<>) {
    my ($dir, $file) = m!(.*/)?([^/]+?)$!;
    Push @{$dup_series_per_dir{$dir||'./'}{lc $file}}, $file;
}

for my $dir (sort keys %dup_series_per_dir) {
    my @all_dup_series_in_dir = grep { @{$_} > 1 } values %{$dup_series_per_dir{$dir}};
    for my $one_dup_series (@all_dup_series_in_dir) {
        print "$dir\{" . join(',', sort @{$one_dup_series}) . "}\n";
    }
}

Versuchen:

ls -1 | tr '[A-Z]' '[a-z]' | sort | uniq -c | grep -v " 1 "

Ganz einfach :-) Sind Pipelines keine wundervollen Bestien?

Der ls -1 gibt Ihnen die Dateien einmal pro Zeile, der tr '[A-Z]' '[a-z]' konvertiert alle Großbuchstaben in Kleinbuchstaben, die sort sortiert sie (überraschenderweise), uniq -c entfernt nachfolgende Vorkommen von doppelten Zeilen, wobei Sie ebenfalls eine Zählung erhalten, und schließlich den grep -v " 1 " entfernt Linien, in denen die Zählung eins war.

Wenn ich dies in einem Verzeichnis mit einem "Duplikat" (ich habe qq in qQ kopiert) ausführen, erhalte ich Folgendes:

2 qq

Ersetzen Sie für die Version "dieses Verzeichnis und jedes Unterverzeichnis" einfach ls -1 durch find . oder find DIRNAME, wenn Sie einen bestimmten Verzeichnisstartpunkt wünschen (DIRNAME ist der Verzeichnisname, den Sie verwenden möchten).

Dies gibt (für mich) zurück:

2 ./.gconf/system/gstreamer/0.10/audio/profiles/mp3
2 ./.gconf/system/gstreamer/0.10/audio/profiles/mp3/%gconf.xml
2 ./.gnome2/accels/blackjack
2 ./qq

welche verursacht werden durch:

pax> ls -1d .gnome2/accels/[bB]* .gconf/system/gstreamer/0.10/audio/profiles/[mM]* [qQ]?
.gconf/system/gstreamer/0.10/audio/profiles/mp3
.gconf/system/gstreamer/0.10/audio/profiles/MP3
.gnome2/accels/blackjack
.gnome2/accels/Blackjack
qq
qQ

Update:

Tatsächlich wird die tr bei weiterer Überlegung alle Komponenten des Pfads in Kleinbuchstaben schreiben, so dass beide

/a/b/c
/a/B/c

werden als Duplikate betrachtet obwohl sie sich in verschiedenen Verzeichnissen befinden .

Wenn nur Duplikate innerhalb eines einzelnen Verzeichnisses als Übereinstimmung angezeigt werden sollen, können Sie das (eher monströse) verwenden:

Perl -ne '
    chomp;
    @flds = split (/\//);
    $lstf = $f[-1];
    $lstf =~ tr/A-Z/a-z/;
    for ($i =0; $i ne $#flds; $i++) {
        print "$f[$i]/";
    };
    print "$x\n";'

anstelle von:

tr '[A-Z]' '[a-z]'

Dabei wird nur der letzte Teil des Pfadnamens in Kleinbuchstaben geschrieben und nicht das Ganze. Wenn Sie nur reguläre Dateien (keine Verzeichnisse, FIFOs usw.) benötigen, können Sie mit find -type f die zurückgegebenen Daten einschränken.

33
paxdiablo

Ich glaube

ls | sort -f | uniq -i -d

ist einfacher, schneller und liefert das gleiche Ergebnis

5
mpez0

Dies ist eine nette kleine Befehlszeilen-App mit dem Namen findsn, die Sie erhalten, wenn Sie fslint kompilieren, das das deb-Paket nicht enthält.

es findet alle Dateien mit demselben Namen und deren Blitz schnell und kann mit unterschiedlichen Fällen umgehen.

/findsn --help
find (files) with duplicate or conflicting names.
Usage: findsn [-A -c -C] [[-r] [-f] paths(s) ...]

Wenn keine Argumente angegeben werden, wird $ PATH nach redundanten Oder in Konflikt stehenden Dateien durchsucht.

-A  reports all aliases (soft and hard links) to files.
    If no path(s) specified then the $PATH is searched.

Wenn nur Pfad (e) angegeben werden, werden sie auf doppelte Dateien mit dem Namen Geprüft. Sie können dies mit -C qualifizieren, um den Fall bei dieser Suche zu ignorieren. Das Qualifizieren mit -c ist restriktiver, da nur Dateien (oder Verzeichnisse) In demselben Verzeichnis gespeichert werden, deren Namen sich nur für den Fall unterscheiden . IE -c kennzeichnet Dateien und Verzeichnisse, die in Konflikt stehen, wenn an ein Dateisystem mit Schreibweise ohne Berücksichtigung der Groß- und Kleinschreibung übertragen wird. Wenn -c oder -C angegeben ist und Keine Pfade angegeben ist, wird das aktuelle Verzeichnis angenommen.

2
user1639307

Hier ein Beispiel, wie Sie alle doppelten JAR-Dateien finden können:

find . -type f -printf "%f\n" -name "*.jar" | sort -f | uniq -i -d

Ersetzen Sie *.jar durch den von Ihnen gesuchten doppelten Dateityp.

2
noclayto

Folgen Sie der Antwort von mpez0. Um rekursiv zu erkennen, ersetzen Sie einfach "ls" durch "find.". Das einzige Problem, das ich dabei sehe, ist, dass, wenn dies ein Verzeichnis ist, das dupliziert, Sie 1 Eintrag haben für jede Datei in diesem Verzeichnis. Ein menschliches Gehirn ist erforderlich, um die Ausgabe davon zu behandeln.

Sie löschen diese Dateien aber nicht automatisch, oder?

find . | sort -f | uniq -i -d
2
Alain

Hier ist ein Skript, das für mich funktioniert hat (ich bin nicht der Autor). Das Original und die Diskussion finden Sie hier: http://www.daemonforums.org/showthread.php?t=4661

#! /bin/sh

# find duplicated files in directory tree
# comparing by file NAME, SIZE or MD5 checksum
# --------------------------------------------
# LICENSE(s): BSD / CDDL
# --------------------------------------------
# vermaden [AT] interia [DOT] pl
# http://strony.toya.net.pl/~vermaden/links.htm

__usage() {
  echo "usage: $( basename ${0} ) OPTION DIRECTORY"
  echo "  OPTIONS: -n   check by name (fast)"
  echo "           -s   check by size (medium)"
  echo "           -m   check by md5  (slow)"
  echo "           -N   same as '-n' but with delete instructions printed"
  echo "           -S   same as '-s' but with delete instructions printed"
  echo "           -M   same as '-m' but with delete instructions printed"
  echo "  EXAMPLE: $( basename ${0} ) -s /mnt"
  exit 1
  }

__prefix() {
  case $( id -u ) in
    (0) PREFIX="rm -rf" ;;
    (*) case $( uname ) in
          (SunOS) PREFIX="pfexec rm -rf" ;;
          (*)     PREFIX="Sudo rm -rf"   ;;
        esac
        ;;
  esac
  }

__crossplatform() {
  case $( uname ) in
    (FreeBSD)
      MD5="md5 -r"
      STAT="stat -f %z"
      ;;
    (Linux)
      MD5="md5sum"
      STAT="stat -c %s"
      ;;
    (SunOS)
      echo "INFO: supported systems: FreeBSD Linux"
      echo
      echo "Porting to Solaris/OpenSolaris"
      echo "  -- provide values for MD5/STAT in '$( basename ${0} ):__crossplatform()'"
      echo "  -- use digest(1) instead for md5 sum calculation"
      echo "       $ digest -a md5 file"
      echo "  -- pfexec(1) is already used in '$( basename ${0} ):__prefix()'"
      echo
      exit 1
    (*)
      echo "INFO: supported systems: FreeBSD Linux"
      exit 1
      ;;
  esac
  }

__md5() {
  __crossplatform
  :> ${DUPLICATES_FILE}
  DATA=$( find "${1}" -type f -exec ${MD5} {} ';' | sort -n )
  echo "${DATA}" \
    | awk '{print $1}' \
    | uniq -c \
    | while read LINE
      do
        COUNT=$( echo ${LINE} | awk '{print $1}' )
        [ ${COUNT} -eq 1 ] && continue
        SUM=$( echo ${LINE} | awk '{print $2}' )
        echo "${DATA}" | grep ${SUM} >> ${DUPLICATES_FILE}
      done

  echo "${DATA}" \
    | awk '{print $1}' \
    | sort -n \
    | uniq -c \
    | while read LINE
      do
        COUNT=$( echo ${LINE} | awk '{print $1}' )
        [ ${COUNT} -eq 1 ] && continue
        SUM=$( echo ${LINE} | awk '{print $2}' )
        echo "count: ${COUNT} | md5: ${SUM}"
        grep ${SUM} ${DUPLICATES_FILE} \
          | cut -d ' ' -f 2-10000 2> /dev/null \
          | while read LINE
            do
              if [ -n "${PREFIX}" ]
              then
                echo "  ${PREFIX} \"${LINE}\""
              else
                echo "  ${LINE}"
              fi
            done
        echo
      done
  rm -rf ${DUPLICATES_FILE}
  }

__size() {
  __crossplatform
  find "${1}" -type f -exec ${STAT} {} ';' \
    | sort -n \
    | uniq -c \
    | while read LINE
      do
        COUNT=$( echo ${LINE} | awk '{print $1}' )
        [ ${COUNT} -eq 1 ] && continue
        SIZE=$( echo ${LINE} | awk '{print $2}' )
        SIZE_KB=$( echo ${SIZE} / 1024 | bc )
        echo "count: ${COUNT} | size: ${SIZE_KB}KB (${SIZE} bytes)"
        if [ -n "${PREFIX}" ]
        then
          find ${1} -type f -size ${SIZE}c -exec echo "  ${PREFIX} \"{}\"" ';'
        else
          # find ${1} -type f -size ${SIZE}c -exec echo "  {}  " ';'  -exec du -h "  {}" ';'
          find ${1} -type f -size ${SIZE}c -exec echo "  {}  " ';'
        fi
        echo
      done
  }

__file() {
  __crossplatform
  find "${1}" -type f \
    | xargs -n 1 basename 2> /dev/null \
    | tr '[A-Z]' '[a-z]' \
    | sort -n \
    | uniq -c \
    | sort -n -r \
    | while read LINE
      do
        COUNT=$( echo ${LINE} | awk '{print $1}' )
        [ ${COUNT} -eq 1 ] && break
        FILE=$( echo ${LINE} | cut -d ' ' -f 2-10000 2> /dev/null )
        echo "count: ${COUNT} | file: ${FILE}"
        FILE=$( echo ${FILE} | sed -e s/'\['/'\\\['/g -e s/'\]'/'\\\]'/g )
        if [ -n "${PREFIX}" ]
        then
          find ${1} -iname "${FILE}" -exec echo "  ${PREFIX} \"{}\"" ';'
        else
          find ${1} -iname "${FILE}" -exec echo "  {}" ';'
        fi
        echo
      done 
  }

# main()

[ ${#} -ne 2  ] && __usage
[ ! -d "${2}" ] && __usage

DUPLICATES_FILE="/tmp/$( basename ${0} )_DUPLICATES_FILE.tmp"

case ${1} in
  (-n)           __file "${2}" ;;
  (-m)           __md5  "${2}" ;;
  (-s)           __size "${2}" ;;
  (-N) __prefix; __file "${2}" ;;
  (-M) __prefix; __md5  "${2}" ;;
  (-S) __prefix; __size "${2}" ;;
  (*)  __usage ;;
esac

Wenn der Suchbefehl für Sie nicht funktioniert, müssen Sie ihn möglicherweise ändern. Zum Beispiel 

OLD :   find "${1}" -type f | xargs -n 1 basename 
NEW :   find "${1}" -type f -printf "%f\n"
1
crafter

Sie können verwenden:

find -type f  -exec readlink -m {} \; | gawk 'BEGIN{FS="/";OFS="/"}{$NF=tolower($NF);print}' | uniq -c

Woher:

  • find -type f
    Rekursion druckt den vollständigen Pfad aller Dateien. 

  • -exec readlink -m {} \;
    Liefert den absoluten Pfad der Datei

  • gawk 'BEGIN{FS="/";OFS="/"}{$NF=tolower($NF);print}'
    Ersetzen Sie alle Dateinamen in Kleinbuchstaben

  • uniq -c
    eindeutig der Pfad, -c gibt die Anzahl der Duplikate aus.

1
user3119102

Etwas zu spät, aber hier ist die Version, mit der ich gegangen bin:

find . -type f | awk -F/ '{print $NF}' | sort -f | uniq -i -d

Hier verwenden wir:

  1. find- Finde alle Dateien unter dem aktuellen Verzeichnis
  2. awk- Entferne den Dateipfad des Dateinamens
  3. sort- Groß-/Kleinschreibung wird nicht unterschieden
  4. uniq- Finde die Dupes von dem, was durch das Rohr kommt

(Inspiriert von der Antwort von @ mpez0 und @SimonDowdles kommentiert die Antwort von @paxdiablo.)

0
serg10

Du kannst Duplikate in einem bestimmten Verzeichnis mit GNU awk überprüfen:

gawk 'BEGINFILE {if ((seen[tolower(FILENAME)]++)) print FILENAME; nextfile}' *

Hiermit wird mit BEGINFILE eine Aktion ausgeführt, bevor eine Datei gelesen und gelesen wird. In diesem Fall verfolgt er die Namen, die in einem Array seen[] erscheinen, dessen Indizes die Namen der Dateien in Kleinbuchstaben sind.

Wenn bereits ein Name aufgetaucht ist, druckt er den Namen. Ansonsten springt es einfach zur nächsten Datei.


Sehen Sie ein Beispiel:

$ tree
.
├── bye.txt
├── hello.txt
├── helLo.txt
├── yeah.txt
└── YEAH.txt

0 directories, 5 files
$ gawk 'BEGINFILE {if ((a[tolower(FILENAME)]++)) print FILENAME; nextfile}' *
helLo.txt
YEAH.txt
0
fedorqui