Comando Bash para listar todos os arquivos com base no conteúdo

3

O que eu estou tentando realizar é pegar todos os arquivos em um diretório e listar / classificá-los pela exclusividade de seu conteúdo

Exemplo:

Digamos que temos esses 7 arquivos em um diretório

uniquefile1.txt, uniquefile2.txt, samefile1.txt, samefile2.txt, equalfile1.txt, equalfile2.txt, equalfile3.txt

em que uniquefile1 e uniquefile2 têm conteúdo diferente, todos os samefile.txt têm o mesmo conteúdo um do outro e todos os equalfile.txt têm o mesmo conteúdo que os outros

Resultado esperado:

uniquefile1.txt
uniquefile2.txt
samefile1.txt, samefile2.txt
equalfile1.txt, equalfile2.txt, equalfile3.txt

Eu tenho andado com hash e usando md5sum, mas não consegui fazer nada para fazer exatamente isso

Eu quero fazer isso usando utilitários como grep, xargs, sed, awk, find e locate misturados com outros coreutils, se necessário.

    
por Hopsain 04.10.2017 / 20:27

2 respostas

1

Esta é uma parte modificada da resposta que escrevi ontem :

$ cksum file* | awk '{ ck[$1$2] = ck[$1$2] ? ck[$1$2] ", " $3 : $3 } END { for (i in ck) print ck[i] }'
file3, file5
file1, file2, file4

No seu caso, você usaria *.txt ou mesmo * (se tudo o que você tem no diretório é o arquivo que deseja comparar), em vez de file* .

O resultado informa que file3 e file5 têm o mesmo conteúdo, como file1 , file2 e file4 (neste exemplo).

O utilitário padrão cksum exibirá três colunas para cada arquivo. A primeira é uma soma de verificação, a segunda é um tamanho de arquivo e a terceira é um nome de arquivo.

O código awk usará a soma de verificação e o tamanho como uma chave na matriz ck e armazenará os nomes dos arquivos que tiverem a mesma chave em uma string separada por vírgula para essa chave. No final, os nomes dos arquivos (seqüência separada por vírgula) são impressos.

A aparência engraçada

ck[$1$2] = ck[$1$2] ? ck[$1$2] ", " $3 : $3

significa apenas "se ck[$1$2] estiver definido como qualquer coisa, atribua ck[$1$2] ", " $3 a ck[$1$2] (anexando um nome de arquivo com uma vírgula intermediária), caso contrário, atribua $3 (é o primeiro nome de arquivo com essa chave ) ".

Para classificar a saída no número de itens em cada lista, passe a saída por

awk -F, '{ print NF, $0 }' | sort -n | cut -d ' ' -f 2-

... como um estágio de pós-processamento. Isso obviamente irá quebrar se qualquer nome de arquivo contiver uma vírgula.

Ou use

cksum file* | awk '{ n[$1$2]++; ck[$1$2] = ck[$1$2] ? ck[$1$2] ", " $3 : $3 } END { for (i in ck) print n[i], ck[i] }' | sort -n | cut -d ' ' -f 2-

que não tem problemas com vírgulas em nomes de arquivos.

Deixe o cut out se quiser ver o número de nomes de arquivos em cada linha de saída.

Para um grande número de arquivos, você pode querer usar

find . -type f -exec cksum {} +

em vez de apenas

cksum * 
    
por 04.10.2017 / 20:43
1

Eu usaria perl :

perl -MDigest::SHA -le '
  for $f (@ARGV) {
    $d = Digest::SHA->new(256);
    $d->addfile($f);
     push @{$h{$d->digest}}, $f
  }
  print join ", ", @{$h{$_}} for keys %h' -- *.txt

Estamos construindo uma matriz associativa cujas chaves são o hash sha256 dos arquivos e o valor da lista de arquivos com esse hash.

Isso facilita classificar a saída por número de ocorrências, por exemplo, com:

perl -MDigest::SHA -le '
  for $f (@ARGV) {
    $d = Digest::SHA->new(256);
    $d->addfile($f);
     push @{$h{$d->digest}}, $f
  }
  print join ", ", @{$h{$_}} for sort {@{$h{$a}} <=> @{$h{$b}}} keys %h' -- *.txt

Ou até mesmo classificar a lista de arquivos em cada conjunto pelo nome do arquivo:

perl -MDigest::SHA -le '
  for $f (@ARGV) {
    $d = Digest::SHA->new(256);
    $d->addfile($f);
     push @{$h{$d->digest}}, $f
  }
  print join ", ", sort {$a cmp $b} @{$h{$_}} for 
    sort {@{$h{$a}} <=> @{$h{$b}}} keys %h' -- *.txt
    
por 04.10.2017 / 22:21

Tags