web-dev-qa-db-fra.com

Comment trouver des fichiers en double avec le même nom mais dans des cas différents qui existent dans le même répertoire sous Linux?

Comment puis-je retourner une liste de fichiers nommés en double, c'est-à-dire qu'ils ont le même nom, mais dans des cas différents qui existent dans le même répertoire?

Je me fiche du contenu des fichiers. J'ai juste besoin de connaître l'emplacement et le nom de tous les fichiers qui ont une copie du même nom.

Exemple de doublons:

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

Idéalement, je dois rechercher tous les fichiers de manière récursive à partir d'un répertoire de base. Dans l'exemple ci-dessus, il s'agissait de /www/

28
Camsoft

L’autre réponse est excellente, mais au lieu du script Perl "plutôt monstrueux", je suggère

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

Ce qui va mettre en minuscule juste la partie du nom de fichier du chemin.

Edit 1: En fait, tout le problème peut être résolu avec:

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

Edit 3: J'ai trouvé une solution utilisant sed, sort et uniq qui permet également d’imprimer les doublons, mais elle ne fonctionne que s’il n’ya pas d’espace dans les noms de fichiers:

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

Edit 2: Et voici un script plus long qui imprimera les noms, il faut une liste de chemins sur stdin, donnée par find. Pas si élégant, mais quand même:

#!/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";
    }
}
37

Essayer:

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

Simple, vraiment :-) Les pipelines ne sont-ils pas des bêtes merveilleuses?

Le ls -1 vous donne les fichiers un par ligne, le tr '[A-Z]' '[a-z]' convertit toutes les majuscules en minuscules, le sort les trie (assez surprenant), uniq -c supprime les occurrences ultérieures des lignes en double tout en vous donnant un compte et, enfin, le grep -v " 1 " supprime celles-ci. lignes où le compte était un.

Quand je lance ceci dans un répertoire avec un "duplicata" (j'ai copié qq dans qQ), je reçois:

2 qq

Pour la version "ce répertoire et chaque sous-répertoire", remplacez simplement ls -1 par find . ou find DIRNAME si vous souhaitez un point de départ de répertoire spécifique (DIRNAME est le nom du répertoire que vous souhaitez utiliser).

Cela retourne (pour moi):

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

qui sont causés par:

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

Mettre à jour:

En fait, après mûre réflexion, la tr minuscule tous composants du chemin afin que les deux

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

seront considérés comme des doublons même s'ils se trouvent dans des répertoires différents .

Si vous souhaitez que les doublons d'un seul répertoire apparaissent sous forme de correspondance, vous pouvez utiliser le (plutôt monstrueux):

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";'

au lieu de:

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

Ce qu’il fait est de ne mettre que la minuscule partie finale du chemin plutôt que le tout. De plus, si vous ne souhaitez que des fichiers normaux (pas de répertoires, FIFO, etc.), utilisez find -type f pour limiter les informations renvoyées.

33
paxdiablo

Je crois

ls | sort -f | uniq -i -d

est plus simple, plus rapide et donnera le même résultat

5
mpez0

Ceci est une jolie petite application en ligne de commande appelée findsn que vous obtenez si vous compilez fslint que le paquet deb ne comprend pas.

il trouvera tous les fichiers portant le même nom, sa rapidité et pourra traiter différents cas.

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

Si aucun argument n'est fourni, $ PATH est recherché pour tout fichier redondant Ou en conflit.

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

Si seuls les chemins spécifiés sont sélectionnés, les fichiers en double nommés Sont vérifiés. Vous pouvez qualifier ceci avec -C pour ignorer la casse dans cette recherche. Qualifier avec -c est plus restrictif, car seuls les fichiers (ou répertoires) Du même répertoire dont les noms ne diffèrent .C'EST À DIRE -c marquera les fichiers et les répertoires qui entreront en conflit s'ils sont transférés vers un système de fichiers insensible à la casse. Remarque Si -c ou -C spécifiés et Aucun chemin (s) spécifié (s), le répertoire en cours est utilisé.

2
user1639307

Voici un exemple pour trouver tous les fichiers jar en double:

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

Remplacez *.jar par le type de fichier en double que vous recherchez.

2
noclayto

Pour faire suite à la réponse de mpez0, pour détecter de manière récursive, remplacez simplement "ls" par "find". Le seul problème que je vois avec ceci est que s'il s'agit d'un répertoire en double, vous avez 1 entrée pour chaque fichier de ce répertoire. Un peu de cerveau humain est nécessaire pour traiter les résultats.

Quoi qu'il en soit, vous ne supprimez pas automatiquement ces fichiers, n'est-ce pas?

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

Voici un script qui a fonctionné pour moi (je ne suis pas l'auteur). l'original et la discussion peuvent être trouvés ici: 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

Si la commande de recherche ne fonctionne pas pour vous, vous devrez peut-être la changer. Par exemple 

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

Vous pouvez utiliser:

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

Où:

  • find -type f
    récursion affiche le chemin complet de tous les fichiers. 

  • -exec readlink -m {} \;
    Obtenir le chemin absolu du fichier

  • gawk 'BEGIN{FS="/";OFS="/"}{$NF=tolower($NF);print}'
    remplace tous les noms de fichiers en minuscules

  • uniq -c
    unique le chemin, -c affiche le nombre de doublons.

1
user3119102

Un peu tard pour celui-ci, mais voici la version que j'ai choisie:

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

Ici nous utilisons:

  1. find- recherche tous les fichiers du répertoire en cours.
  2. awk- supprime le chemin du fichier dans le nom du fichier
  3. sort- trier la casse de manière insensible
  4. uniq- trouve les dupes à partir de ce qui les fait traverser le tuyau

(Inspiré par la réponse @ mpez0 et la remarque @SimonDowdles sur la réponse @paxdiablo.)

0
serg10

Vous pouvez vérifier les doublons dans un répertoire donné avec GNU awk:

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

Ceci utilise BEGINFILE pour effectuer une action avant de continuer et de lire un fichier. Dans ce cas, il garde une trace des noms apparus dans un tableau seen[] dont les index sont les noms des fichiers en minuscules.

Si un nom est déjà apparu, quel que soit le cas, il l’imprime. Sinon, il saute juste au fichier suivant.


Voir un exemple:

$ 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