Szukasz zduplikowanych nazw plików w hierarchii folderów?

29

Mam folder o nazwie img, ten folder ma wiele poziomów podfolderów, z których wszystkie zawierają obrazy. Zamierzam je zaimportować na serwer obrazów.

Zwykle obrazy (lub dowolne pliki) mogą mieć tę samą nazwę, o ile znajdują się w innej ścieżce katalogu lub mają inne rozszerzenie. Jednak serwer obrazów, do którego importuję je, wymaga, aby wszystkie nazwy obrazów były unikalne (nawet jeśli rozszerzenia są różne).

Na przykład obrazy background.pngi background.gifnie byłyby dozwolone, ponieważ mimo że mają różne rozszerzenia, nadal mają tę samą nazwę pliku. Nawet jeśli znajdują się w osobnych podfolderach, nadal muszą być unikalne.

Zastanawiam się więc, czy mogę przeprowadzić wyszukiwanie cykliczne w imgfolderze, aby znaleźć listę plików o tej samej nazwie (bez rozszerzenia).

Czy istnieje polecenie, które może to zrobić?

JD Isaacks
źródło
@DavidFoerster Masz rację! Nie mam pojęcia, dlaczego myślałem, że może to być duplikat Jak znaleźć (i usunąć) duplikaty plików , ale najwyraźniej tak nie jest.
Eliah Kagan

Odpowiedzi:

17

FSlint Zainstaluj fslint to wszechstronna wyszukiwarka duplikatów, która zawiera funkcję wyszukiwania duplikatów nazw:

FSlint

Pakiet FSlint dla Ubuntu podkreśla interfejs graficzny, ale jak wyjaśniono w FAQ FSlint, interfejs wiersza poleceń jest dostępny za pośrednictwem programów w /usr/share/fslint/fslint/. Użyj --helpopcji dla dokumentacji, np .:

$ /usr/share/fslint/fslint/fslint --help
File system lint.
A collection of utilities to find lint on a filesystem.
To get more info on each utility run 'util --help'.

findup -- find DUPlicate files
findnl -- find Name Lint (problems with filenames)
findu8 -- find filenames with invalid utf8 encoding
findbl -- find Bad Links (various problems with symlinks)
findsn -- find Same Name (problems with clashing names)
finded -- find Empty Directories
findid -- find files with dead user IDs
findns -- find Non Stripped executables
findrs -- find Redundant Whitespace in files
findtf -- find Temporary Files
findul -- find possibly Unused Libraries
zipdir -- Reclaim wasted space in ext2 directory entries
$ /usr/share/fslint/fslint/findsn --help
find (files) with duplicate or conflicting names.
Usage: findsn [-A -c -C] [[-r] [-f] paths(s) ...]

If no arguments are supplied the $PATH is searched for any redundant
or conflicting files.

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

If only path(s) specified then they are checked for duplicate named
files. You can qualify this with -C to ignore case in this search.
Qualifying with -c is more restictive as only files (or directories)
in the same directory whose names differ only in case are reported.
I.E. -c will flag files & directories that will conflict if transfered
to a case insensitive file system. Note if -c or -C specified and
no path(s) specifed the current directory is assumed.

Przykładowe użycie:

$ /usr/share/fslint/fslint/findsn /usr/share/icons/ > icons-with-duplicate-names.txt
$ head icons-with-duplicate-names.txt 
-rw-r--r-- 1 root root    683 2011-04-15 10:31 Humanity-Dark/AUTHORS
-rw-r--r-- 1 root root    683 2011-04-15 10:31 Humanity/AUTHORS
-rw-r--r-- 1 root root  17992 2011-04-15 10:31 Humanity-Dark/COPYING
-rw-r--r-- 1 root root  17992 2011-04-15 10:31 Humanity/COPYING
-rw-r--r-- 1 root root   4776 2011-03-29 08:57 Faenza/apps/16/DC++.xpm
-rw-r--r-- 1 root root   3816 2011-03-29 08:57 Faenza/apps/22/DC++.xpm
-rw-r--r-- 1 root root   4008 2011-03-29 08:57 Faenza/apps/24/DC++.xpm
-rw-r--r-- 1 root root   4456 2011-03-29 08:57 Faenza/apps/32/DC++.xpm
-rw-r--r-- 1 root root   7336 2011-03-29 08:57 Faenza/apps/48/DC++.xpm
-rw-r--r-- 1 root root    918 2011-03-29 09:03 Faenza/apps/16/Thunar.png
ændrük
źródło
Dzięki, to działało. Niektóre wyniki są w kolorze fioletowym, a niektóre w kolorze zielonym. Czy wiesz, co oznaczają różne kolory?
JD Isaacks
@John Wygląda na to, że FSlint używa ls -ldo sformatowania danych wyjściowych. To pytanie powinno wyjaśnić, co oznaczają kolory.
ændrük
FSlint ma wiele zależności.
Navin
31
find . -mindepth 1 -printf '%h %f\n' | sort -t ' ' -k 2,2 | uniq -f 1 --all-repeated=separate | tr ' ' '/'

Jak stwierdzono w komentarzu, to również znajdzie foldery. Oto polecenie, aby ograniczyć go do plików:

find . -mindepth 1 -type f -printf '%p %f\n' | sort -t ' ' -k 2,2 | uniq -f 1 --all-repeated=separate | cut -d' ' -f1
ojblass
źródło
Zmieniłem rozwiązanie, aby zwracało pełną (względną) ścieżkę wszystkich duplikatów. Niestety zakłada się, że nazwy ścieżek nie zawierają białych znaków, ponieważ uniqnie zapewniają funkcji wyboru innego separatora pola.
David Foerster,
@DavidFoerster, twój rev 6 był poprawą, ale jeśli chodzi o twój komentarz tam, od kiedy jest sedprzestarzały? Tajemna? Pewnie. Przestarzały? Nie, że jestem świadomy. (I właśnie szukałem, aby sprawdzić.)
cp.engr
@ cp.engr: sed nie jest przestarzały. To wywołanie stało się nieaktualne po mojej kolejnej zmianie.
David Foerster,
@DavidFoerster, przestarzałe nie wydaje mi się zatem właściwym słowem. Myślę, że lepszym rozwiązaniem byłoby „pominięcie”. Niezależnie od tego, dziękuję za wyjaśnienie.
cp.engr
@ cp.engr: Dzięki za sugestię! Nie znałem tego słowa, ale wydaje się, że lepiej pasuje do sytuacji.
David Foerster,
8

Zapisz to w pliku o nazwie duplicates.py

#!/usr/bin/env python

# Syntax: duplicates.py DIRECTORY

import os, sys

top = sys.argv[1]
d = {}

for root, dirs, files in os.walk(top, topdown=False):
    for name in files:
        fn = os.path.join(root, name)
        basename, extension = os.path.splitext(name)

        basename = basename.lower() # ignore case

        if basename in d:
            print(d[basename])
            print(fn)
        else:
            d[basename] = fn

Następnie uczyń plik wykonywalnym:

chmod +x duplicates.py

Uruchom np. W ten sposób:

./duplicates.py ~/images

Powinien wypisywać pary plików o tej samej nazwie basename (1). Napisane w Pythonie, powinieneś być w stanie je modyfikować.

Loevborg
źródło
Wygląda na to, że nie działa poprawnie. Wykrywa P001.ORFi P001 (1).ORFduplikuje, a także wydaje się myśleć, że 60% moich plików to duplikaty, co jest błędne, jestem pewien. fslintznaleziono realną liczbę zduplikowanych nazw plików, która jest bliska 3%.
Rolf
3

Zakładam, że potrzebujesz tylko tych „duplikatów”, a następnie obsłużyć je ręcznie. Jeśli tak, to ten kod bash4 powinien robić, co chcesz.

declare -A array=() dupes=()
while IFS= read -r -d '' file; do 
    base=${file##*/} base=${base%.*}
    if [[ ${array[$base]} ]]; then 
        dupes[$base]+=" $file"
    else
        array[$base]=$file
    fi
done < <(find /the/dir -type f -print0)

for key in "${!dupes[@]}"; do 
    echo "$key: ${array[$key]}${dupes[$key]}"
done

Zobacz http://mywiki.wooledge.org/BashGuide/Arrays#Associative_Arrays i / lub instrukcję bash, aby uzyskać pomoc na temat składni tablic asocjacyjnych.

geirha
źródło
Jak wykonać takie polecenie w terminalu? Czy to coś, co muszę najpierw zapisać do pliku i uruchomić plik?
JD Isaacks,
@John Isaacks Możesz skopiować / wkleić go do terminala lub umieścić w pliku i uruchomić jako skrypt. Każdy przypadek osiągnie to samo.
geirha
1

To jest bname:

#!/bin/bash
#
#  find for jpg/png/gif more files of same basename 
#
# echo "processing ($1) $2"
bname=$(basename "$1" .$2)
find -name "$bname.jpg" -or -name "$bname.png"

Spraw, by był wykonywalny:

chmod a+x bname 

Wywołaj to:

for ext in jpg png jpeg gif tiff; do find -name "*.$ext" -exec ./bname "{}" $ext ";"  ; done

Zawodowiec:

  • Jest prosty i prosty, dlatego można go rozszerzać.
  • Obsługuje spacje, tabulatory, podziały wierszy i kanały stron w nazwach plików, afaik. (Zakładając, że nie ma czegoś takiego w nazwie rozszerzenia).

Kon:

  • Znajduje zawsze sam plik, a jeśli znajdzie a.gif dla a.jpg, znajdzie również a.jpg dla a.gif. Zatem dla 10 plików o tej samej nazwie basename znajduje na końcu 100 dopasowań.
nieznany użytkownik
źródło
0

Ulepszenie skryptu loevborga, na moje potrzeby (zawiera zgrupowane dane wyjściowe, czarną listę, czystsze dane wyjściowe podczas skanowania). Skanowałem dysk 10 TB, więc potrzebowałem nieco czystszego wyjścia.

Stosowanie:

python duplicates.py DIRNAME

duplicates.py

    #!/usr/bin/env python

    # Syntax: duplicates.py DIRECTORY

    import os
    import sys

    top = sys.argv[1]
    d = {}

    file_count = 0

    BLACKLIST = [".DS_Store", ]

    for root, dirs, files in os.walk(top, topdown=False):
        for name in files:
            file_count += 1
            fn = os.path.join(root, name)
            basename, extension = os.path.splitext(name)

            # Enable this if you want to ignore case.
            # basename = basename.lower()

            if basename not in BLACKLIST:
                sys.stdout.write(
                    "Scanning... %s files scanned.  Currently looking at ...%s/\r" %
                    (file_count, root[-50:])
                )

                if basename in d:
                    d[basename].append(fn)
                else:
                    d[basename] = [fn, ]

    print("\nDone scanning. Here are the duplicates found: ")

    for k, v in d.items():
        if len(v) > 1:
            print("%s (%s):" % (k, len(v)))
            for f in v:
                print (f)
skoczen
źródło