Localizando arquivos duplicados

Há inúmeras abordagens para tratarmos arquivos duplicados, mas fazê-lo é sempre uma chateação porque em geral é como o “cachorro correndo atrás do próprio rabo”. Centenas de pessoas enviando arquivos para o mesmo servidor, alterando reenviando, copiando … em pouco tempo, se o volume de arquivos trabalhados for grande você pode ter uma imensa quantidade de arquivos duplicados. O que vou tratar aqui são duas formas distintas, uma usando hardlinks e outra efetivamente excluindo as duplicidades.

Arquivos duplicados

Antes de mais nada há de se definir o que são arquivos duplicados. O que pode parecer obvio para alguns pode não ser exatamente o que estamos tentando evitar.

Arquivos duplicados são aqueles que possuem exatamente o mesmo conteúdo (igualdade bit-a-bit), independente do seu nome ou caminho.

Por exemplo, se você criar um arquivo qualquer e pelo gerenciador de arquivos der CTRL-C e CTRL-V, ou se copiar os arquivos de um pendrive para uma pasta dentro de Documentos e depois o fizer outra vez para sua Área de Trabalho, estes serão exemplos de arquivos duplicados bit-a-bit.

Caso você crie um arquivo de imagem e depois salve uma cópia com resolução menor, embora tenham a mesma imagem, eles não são duplicados bit-a-bit. Da mesma forma se você já tiver dois arquivos de música MP3, WMA etc que sejam duplicatas um do outro e alterar as tags de “Titulo”, “Album”, “Autor” etc estes arquivos não estarão duplicados bit-a-bit. Embora seja possível detectá-los estes casos não serão abordados neste texto.

E por fim, dois arquivos chamados RECEITA DE BOLO.txt em pastas diferentes e com o conteúdo diferente não são arquivos duplicados.

Como tratar arquivos duplicados

O procedimento para tratar arquivos duplicados é simples:

1- Localizar quem é duplicata de quem;

2 – Escolher quem será preservado em cada grupo de duplicatas;

3 – Decidir o que será feito com as duplicatas.

A primeira parte requer que se veja todos os arquivos desejados para gerar uma listagem de quem está de fato duplicado.  A forma mais comum de se localizar arquivos no Linux é usando o comando “find” com suas dezenas de opções de busca, mas infelizmente nenhuma delas permite localizar duplicatas, então se optar esta alternativa você terá um belo esforço braçal com os comandos md5sum, grep, sed, sort, uniq etc.

A segunda vai depender das suas necessidades e vontade. Pode ser o primeiro arquivo da lista, ou pode ser o mais recente, o mais antigo, o de nome mais curto, mais longo etc.

A terceira etapa geralmente se resume a excluir as duplicidades, mas você poderia optar por criar hardlinks ou mesmo links simbólicos para o que foi preservado.

Substituindo arquivos duplicados por hardlinks

Hardlink é uma entrada em um diretório que associa um nome a um arquivo no seu disco. Alguns sistema de arquivos permitem a criação de múltiplos hardlinks para o mesmo arquivo, mesmo em diretórios diferentes (vide Hardlinks na Wikipedia), como é o caso dos sistemas de arquivo EXT2, EXT3, EXT4, BTRFS entre outros.

Se um hardlink nada mais é do que outro nome para o mesmo arquivo e este hardlink não ocupa espaço em disco (ao menos não um espaço significativo) então é uma grande jogada você substituir suas duplicatas por hadlinks. Para o usuário final é como se o arquivo original estivesse lá, mas para o seu disco você estará dando uma economia proporcional a cada duplicata convertida.

Talvez você já conheça o comando para criar hardlinks no Linux, o comando “ln”, no entanto você teria que já saber de antemão quem é duplicata de quem (o primeiro passo que descrevi lá em cima) usando “find” com “md5sum” etc.

Ao invés disso, usaremos o comando “hardlink”, um pouco menos conhecido, porém disponível no repositório oficial das principais distribuições Linux. em sistemas baseados em Debian e Ubuntu ele pode ser instalado pelo comando:

$ sudo apt-get install hardlink

A vantagem deste comando em relação ao “ln” é que este último já faz todo o “trabalho sujo” de verificar as duplicidades dentro de um diretório e criar os links corretamente, sem necessidade de usar o find ou algo parecido.

Para exemplificar eu copiei uns arquivos para um diretório chamado TESTE, fiz algumas copias dos mesmos arquivos (apenas alterando os nomes) e obtive o seguinte resultado:

$ ls -lh TESTE
total 467M
-rw------- 1 wbraga wbraga 4,4M Ago 24 15:45 10_-_Seu_balancê.mp3
-rw-rw-r-- 1 wbraga wbraga 5,0M Ago 24 15:47 bios-pc-casa.zip
-rw-rw-r-- 1 wbraga wbraga 5,0M Ago 24 15:46 mb_bios_ga-h87m-hd3_f9.zip
-rw-rw-r-- 1 wbraga wbraga  41M Ago 24 15:46 thunderbird-45.1.0.tar.bz2
-rw-rw-r-- 1 wbraga wbraga  41M Ago 24 15:46 thunderbird.tbz
-rwxr-xr-x 1 wbraga wbraga 121M Ago 24 15:48 warriorsofthenet.mpeg
-rwxr-xr-x 1 wbraga wbraga 121M Ago 24 15:44 Warriors of the Net.mpeg
-rwxr-xr-x 1 wbraga wbraga 121M Ago 24 15:47 Warriors-of-the-Net.mpeg
-rw------- 1 wbraga wbraga 4,4M Ago 24 15:47 zecapagodinho.mp3
-rw------- 1 wbraga wbraga 4,4M Ago 24 15:47 zecapagodinho-seubalance.mp3

$ du -sh TESTE
467M    TESTE

Observe ai que eu tenho vários arquivos duplicados consumindo 467MB no disco (saída do comando “du”). Ao rodar o comando “hardlink” nesta pasta, conforme exemplo abaixo.

$ hardlink TESTE
Mode:     real
Files:    10
Linked:   6 files
Compared: 0 xattrs
Compared: 6 files
Saved:    295,64 MiB
Duration: 0,11 seconds

Veja que em menos de 1 segundo ele reconheceu que dos 10 arquivos ali existentes 6 arquivos eram duplicados e podiam ser convertidos em hardlink, economizando cerca de 295MB.

Não vou colocar a saída do comando “ls” novamente pois como disse antes para o usuário será como se os arquivos originais estivessem lá, então não há mudança, mas para o disco houve uma economia de espaço como se vê na saída do comando “du” realizada logo em seguida.

$ du -sh TESTE
171M    TESTE

O comando hardlink possui uma dezena de parâmetros simples de serem usados e que podem ser consultados no seu help, ou página de manual, mas um parâmetro que você gostará de saber que existe é o “-n” que apenas simula o que seria feito, e não altera nada no seu disco.

Excluindo arquivos duplicados

Em muitos casos pode ser preferível excluir as duplicidades, para tal feito, sem recorrer “as receitas de magia negra” do bash, usando diversos comandos, pode-se usar apenas a ferramenta “fdupes”.

Fdupes é uma ferramenta para tratar duplicatas bit-a-bit, tal como o comando “hardlink”, mas ao invés de substituir as duplicatas por hardlinks ele permite a exclusão dos arquivos indesejados, de forma interativa, ou de forma automática.

O seu pacote também está disponível nas principais distribuições Linux podendo ser instalado diretamente a partir dos repositórios oficiais. Como no caso dos “Debian-like” que usa-se o comandos apt-get:

$ sudo apt-get install fdupes

Uma vez instalado você pode executá-lo diretamente apenas informando a pasta que será analisada. Como no exemplo abaixo, onde uso um diretório com conteúdo similar ao usado no exemplo do “hardlink”.

$ fdupes  TESTE2
TESTE2/thunderbird.tbz                  
TESTE2/thunderbird-45.1.0.tar.bz2

TESTE2/warriorsofthenet.mpeg
TESTE2/Warriors of the Net.mpeg
TESTE2/Warriors-of-the-Net.mpeg

TESTE2/mb_bios_ga-h87m-hd3_f9.zip
TESTE2/bios-pc-casa.zip

TESTE2/zecapagodinho.mp3
TESTE2/zecapagodinho-seubalance.mp3
TESTE2/10_-_Seu_balancê.mp3

Veja que sem informar nenhum parâmetro o comando “fdupes” apenas lista os arquivos duplicados agrupados para que você tenha uma noção da situação.

Se quiser mais informações há os parâmetros “-S” (–size) que exibe um cabeçalho acima de cada grupo de arquivos, informando o tamanho de cada arquivo (lembrando que todos os arquivos são idênticos bit-a-bit, portanto possuem o mesmo tamanho), ou ainda o parâmetro “-m” (–summarize) que ao invés de listar os arquivos dá um resumo de uma linha apenas informando quantos arquivos foram encontrados duplicados em quantos grupos e o quanto é usado em disco por eles.

Agora que sabemos quais arquivos estão duplicados podemos removê-los usando o parâmetro “-d” (–delete).

$ fdupes -d TESTE2
[1] TESTE2/thunderbird.tbz              
[2] TESTE2/thunderbird-45.1.0.tar.bz2

Set 1 of 4, preserve files [1 - 2, all]:

Veja que o parâmetro “-d” permite remoção dos arquivos de forma interativa. Ele informa que este é “conjunto 1” dos 4 conjuntos de duplicatas e espera que você informe qual dos dois arquivos neste primeiro conjunto você deseja preservar. Como aqui só tenho 2 arquivos as opções seriam “1”, “2” ou “all” se não quiser excluir nenhum arquivo neste grupo.

Para demonstrar, eu digitei “all” e teclei ENTER. Veja a saída do comando fdupes:

Set 1 of 4, preserve files [1 - 2, all]: all

   [+] TESTE2/thunderbird.tbz
   [+] TESTE2/thunderbird-45.1.0.tar.bz2

[1] TESTE2/warriorsofthenet.mpeg
[2] TESTE2/Warriors of the Net.mpeg
[3] TESTE2/Warriors-of-the-Net.mpeg

Set 2 of 4, preserve files [1 - 3, all]:

Ele informa que ambos os arquivos foram preservados (sinal “+”) e passa para o grupo 2, exibindo seus 3 arquivos e esperando que eu decida qual será preservado, tal como no primeiro.

Set 2 of 4, preserve files [1 - 3, all]: 3

   [-] TESTE2/warriorsofthenet.mpeg
   [-] TESTE2/Warriors of the Net.mpeg
   [+] TESTE2/Warriors-of-the-Net.mpeg

[1] TESTE2/mb_bios_ga-h87m-hd3_f9.zip
[2] TESTE2/bios-pc-casa.zip

Set 3 of 4, preserve files [1 - 2, all]:

Veja acima o que acontece quando eu escolho um arquivo para preservar (no caso, o arquivo 3). Os demais arquivos foram excluídos (sinal “-“) e passamos ao terceiro grupo.

Isso ocorrerá para cada grupo de arquivo, sejam eles quantos forem, então dá pra imaginar o tempo que você perderá confirmando todos os grupos, caso hajam algumas centenas de arquivos duplicados.

Caso você queira excluir todos os arquivos sem duplicados sem que o fdupes peça uma confirmação para cada grupo de arquivo você deve estar extremamente seguro do que está fazendo e então usar o parâmetro “-d”, já usado, seguido do parâmetro “-N” (–noprompt) que como o nome sugere fará com que não haja uma pergunta sobre qual preservar. Neste caso, o fdupes preservará o primeiro arquivo de cada grupo e excluirá o restante.

$ fdupes -d -N TESTE2
                                        
   [+] TESTE2/thunderbird.tbz
   [-] TESTE2/thunderbird-45.1.0.tar.bz2


   [+] TESTE2/mb_bios_ga-h87m-hd3_f9.zip
   [-] TESTE2/bios-pc-casa.zip


   [+] TESTE2/zecapagodinho.mp3
   [-] TESTE2/zecapagodinho-seubalance.mp3
   [-] TESTE2/10_-_Seu_balancê.mp3

Esta é a saída do comando fdupes para uma exclusão não interativa usando os parâmetros “-d” e “-N”. Lembrando que + significa preservado e “-” significa excluído.

Feito isso seu sistema não terá mais arquivos duplicados e o processo todo ocorre bem rápido considerando o tempo que outros métodos levariam para fazer a mesma coisa.

O fdupes possui apenas mais meia dúzia de parâmetros mas que podem ser úteis. Por exemplo:

-r (–recurse) – para que você faça uma busca recursiva, ou seja dentro do diretório informado e de todos os demais dentro do primeiro. Por padrão se você não informar este parâmetro o fdupes não irá entrar nos subdiretórios;

-s (–symlinks) – Por padrão o fdupes não considera um link simbólico como duplicata, este parâmetro o força a considerar como tal. Tenha extremo cuidado ao usar este parâmetro com o “-d” ou você poderá excluir seu arquivo original e ficar apenas com um symlink que aponta para lugar nenhum;

-H (–hardliks) – Tal como o o anterior, porém para hardlinks. Como hardlinks é exatamente o mesmo arquivo em outro local ou com outro nome, não há perigo em excluí-los caso queira;

-n – Não considera que arquivos vazios (tamanho de 0 bytes) sejam duplicatas. Por padrão eles serão considerados e você poderá ter um grupo gigante com centenas de arquivos de tamanho zero;

-A (–nohidden) Não verifica arquivos ocultos (aqueles que iniciam o nome com um “.”),  tal como “.bashrc”, “.gitignore” etc;

-1 (–sameline) – Lista todos os arquivos de cada grupo em uma só linha. Este parâmetro é extremamente útil se ao invés de excluir os arquivos, você quiser levar a saída do fdupes para tratamento por outro programa ou script.  Por exemplo se você quiser preservar apenas o arquivo mais recente de cada grupo, ou o mais antigo, ou enviar um email para cada grupo de arquivo excluído, fazer uma condicional para que arquivos de determinada pasta ou contendo determinado nome sejam preservados etc. Nada disso é possível diretamente com o fdupes, mas como todos os arquivos de cada grupo estão em uma só linha você pode facilmente usar o “|” para redirecionar sua saída para outro programa ou script que faça isso.

Algo mais

Este texto não esgota o assunto. Como disse há muitas maneiras de se tratar arquivos duplicados. De todas estas foram as que achei mais praticas para lidar com alguns milhões de grupo de arquivos duplicados, consumindo mais de 10TB (dez terabytes) em disco, desnecessariamente, e que funcionou de maneira efetiva. Ou seja, se pra esse volume todo de dados  eles funcionaram bem, você pode usá-lo seguramente em sua biblioteca de músicas, fotos e documentos.

E lembre-se sempre, faça um backup antes de fazer algo que possa comprometer seus dados.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *