Procurar nomes de arquivos duplicados na hierarquia de pastas?

29

Eu tenho uma pasta chamada img, esta pasta tem muitos níveis de subpastas, todas contendo imagens. Vou importá-los para um servidor de imagem.

Normalmente, as imagens (ou qualquer arquivo) podem ter o mesmo nome, desde que estejam em um caminho de diretório diferente ou em uma extensão diferente. No entanto, o servidor de imagem para o qual estou importando exige que todos os nomes de imagens sejam exclusivos (mesmo que as extensões sejam diferentes).

Por exemplo, as imagens background.pnge background.gifnão seriam permitidas porque, embora tenham extensões diferentes, ainda têm o mesmo nome de arquivo. Mesmo se estiverem em subpastas separadas, ainda precisam ser exclusivas.

Por isso, estou pensando se posso fazer uma pesquisa recursiva na imgpasta para encontrar uma lista de arquivos com o mesmo nome (excluindo a extensão).

Existe um comando que pode fazer isso?

JD Isaacks
fonte
@DavidFoerster Você está certo! Não tenho ideia de por que pensei que isso poderia ser uma duplicata de Como encontrar (e excluir) arquivos duplicados , mas claramente não é.
Eliah Kagan 17/08/19

Respostas:

17

O FSlint Instale o fslint é um localizador duplicado versátil que inclui uma função para encontrar nomes duplicados:

FSlint

O pacote FSlint para Ubuntu enfatiza a interface gráfica, mas como é explicado nas Perguntas frequentes do FSlint, uma interface de linha de comando está disponível através dos programas em /usr/share/fslint/fslint/. Use a --helpopção para documentação, por exemplo:

$ /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.

Exemplo de uso:

$ /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
fonte
Graças isso funcionou. Alguns dos resultados estão em roxo e outros em verde. Você sabe de imediato o que significam as diferentes cores?
perfil completo de JD Isaacks
@ John Parece que o FSlint está usando ls -lpara formatar sua saída. Esta pergunta deve explicar o que as cores significam.
ændrük
O FSlint tem muitas dependências.
Navin
31
find . -mindepth 1 -printf '%h %f\n' | sort -t ' ' -k 2,2 | uniq -f 1 --all-repeated=separate | tr ' ' '/'

Como o comentário afirma, isso também encontrará pastas. Aqui está o comando para restringi-lo aos arquivos:

find . -mindepth 1 -type f -printf '%p %f\n' | sort -t ' ' -k 2,2 | uniq -f 1 --all-repeated=separate | cut -d' ' -f1
ojblass
fonte
Alterei a solução para que ela retorne o caminho completo (relativo) de todas as duplicatas. Infelizmente, pressupõe que os nomes dos caminhos não contenham espaço em branco porque uniqnão fornece um recurso para selecionar um delimitador de campo diferente.
David Foerster
@DavidFoerster, sua rev 6 foi uma melhoria, mas em relação ao seu comentário lá, desde quando sedobsoleto? Arcano? Certo. Obsoleto? Não que eu saiba. (E eu só procurou a seleção.)
cp.engr
@ cp.engr: sed não é obsoleto. Sua invocação tornou-se obsoleta após outra mudança minha.
precisa
@DavidFoerster, obsoleto não parece a palavra certa para mim, então. Eu acho que "evitado" seria um ajuste melhor. Independentemente disso, obrigado por esclarecer.
cp.engr
@ cp.engr: Obrigado pela sugestão! Eu não conhecia essa palavra, mas ela parece se encaixar melhor na situação.
precisa
8

Salve isso em um arquivo chamado 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

Em seguida, torne o arquivo executável:

chmod +x duplicates.py

Execute, por exemplo, o seguinte:

./duplicates.py ~/images

Ele deve gerar pares de arquivos com o mesmo nome de base (1). Escrito em python, você deve poder modificá-lo.

Loevborg
fonte
Parece não funcionar corretamente. Ele detecta P001.ORFe P001 (1).ORFé duplicado e também parece pensar que 60% dos meus arquivos são duplicados, o que está errado, tenho certeza. fslintencontrou um número realístico de nomes de arquivos duplicados que é próximo de 3%.
Rolf
3

Suponho que você só precise ver essas "duplicatas" e manipulá-las manualmente. Nesse caso, esse código bash4 deve fazer o que você deseja, eu acho.

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

Consulte http://mywiki.wooledge.org/BashGuide/Arrays#Associative_Arrays e / ou o manual do bash para obter ajuda sobre a sintaxe da matriz associativa.

Geirha
fonte
Como executo um comando como esse em um terminal? É algo que preciso salvar primeiro em um arquivo e executá-lo?
JD Isaacks
@ John Isaacks Você pode copiar / colar no terminal ou colocá-lo em um arquivo e executá-lo como um script. Qualquer um dos casos alcançará o mesmo.
geirha
1

Este é 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"

Torne executável:

chmod a+x bname 

Invoque-o:

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

Pró:

  • É direto e simples, portanto extensível.
  • Manipula espaços em branco, guias, quebras de linha e feeds de página nos nomes de arquivos, no máximo. (Supondo que não exista tal coisa no nome da extensão).

Vigarista:

  • Ele sempre encontra o próprio arquivo e, se encontrar a.gif para a.jpg, também encontrará a.jpg para a.gif. Portanto, para 10 arquivos com o mesmo nome de base, ele encontra 100 correspondências no final.
Usuário desconhecido
fonte
0

Melhoria no script de loevborg, para minhas necessidades (inclui saída agrupada, lista negra, saída mais limpa durante a digitalização). Eu estava digitalizando uma unidade de 10 TB, então precisava de uma saída um pouco mais limpa.

Uso:

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
fonte