Como posso comparar dois diretórios recursivamente e verificar se um dos diretórios contém o outro?

2

Eu tenho dois diretórios, eles contêm arquivos comuns. Eu quero saber se um diretório contém o mesmo arquivo que o outro. Eu encontrei um script na net, mas eu preciso melhorar para fazer recursivamente.

  #!/bin/bash

  # cmp_dir - program to compare two directories

  # Check for required arguments
  if [ $# -ne 2 ]; then
      echo "usage: $0 directory_1 directory_2" 1>&2
      exit 1
  fi

  # Make sure both arguments are directories
  if [ ! -d $1 ]; then
      echo "$1 is not a directory!" 1>&2
      exit 1
  fi

  if [ ! -d $2 ]; then
      echo "$2 is not a directory!" 1>&2
      exit 1
  fi

  # Process each file in directory_1, comparing it to directory_2
  missing=0
  for filename in $1/*; do
      fn=$(basename "$filename")
      if [ -f "$filename" ]; then
          if [ ! -f "$2/$fn" ]; then
              echo "$fn is missing from $2"
              missing=$((missing + 1))
          fi
      fi
  done
  echo "$missing files missing"

Alguém sugeriria um algoritmo para isso?

    
por kenn 25.05.2014 / 22:23

3 respostas

7
#!/bin/bash

# cmp_dir - program to compare two directories

# Check for required arguments
if [ $# -ne 2 ]; then
  echo "usage: $0 directory_1 directory_2" 1>&2
  exit 1
fi

# Make sure both arguments are directories
if [ ! -d "$1" ]; then
  echo "$1 is not a directory!" 1>&2
  exit 1
fi

if [ ! -d "$2" ]; then
  echo "$2 is not a directory!" 1>&2
  exit 1
fi

# Process each file in directory_1, comparing it to directory_2
missing=0
while IFS= read -r -d $'
while IFS= read -r -d $'
while IFS= read -r -d $'
#!/bin/bash

# cmp_dir - program to compare two directories

# Check for required arguments
if [ $# -ne 2 ]; then
  echo "usage: $0 directory_1 directory_2" 1>&2
  exit 1
fi

# Make sure both arguments are directories
if [ ! -d "$1" ]; then
  echo "$1 is not a directory!" 1>&2
  exit 1
fi

if [ ! -d "$2" ]; then
  echo "$2 is not a directory!" 1>&2
  exit 1
fi

# Process each file in directory_1, comparing it to directory_2
missing=0
while IFS= read -r -d $'
while IFS= read -r -d $'
while IFS= read -r -d $'%pre%' filename
do
  fn=$(basename "$filename")
  if ! find "$2" -name "$fn" | grep -q . ; then
      echo "$fn is missing from $2"
      missing=$((missing + 1))
  fi
done < <(find "$1" -type f -print0)
' filename do fn=${filename#$1} if [ ! -f "$2/$fn" ]; then echo "$fn is missing from $2" missing=$((missing + 1)) fi done < <(find "$1" -type f -print0)
' filename do fn=${filename#$1} if [ ! -f "$2/$fn" ]; then echo "$fn is missing from $2" missing=$((missing + 1)) fi done < <(find "$1" -type f -print0) echo "$missing files missing"
' filename do fn=$(basename "$filename") if ! find "$2" -name "$fn" | grep -q . ; then echo "$fn is missing from $2" missing=$((missing + 1)) fi done < <(find "$1" -type f -print0)
' filename do fn=${filename#$1} if [ ! -f "$2/$fn" ]; then echo "$fn is missing from $2" missing=$((missing + 1)) fi done < <(find "$1" -type f -print0)
' filename do fn=${filename#$1} if [ ! -f "$2/$fn" ]; then echo "$fn is missing from $2" missing=$((missing + 1)) fi done < <(find "$1" -type f -print0) echo "$missing files missing"

Observe que adicionei aspas duplas em torno de $1 e $2 em vários locais acima para protegê-las da expansão do shell. Sem as aspas duplas, os nomes de diretório com espaços ou outros caracteres difíceis causariam erros.

O loop de chaves agora é:

%pre%

Isso usa find para mergulhar recursivamente no diretório $1 e localizar nomes de arquivos. A construção while IFS= read -r -d $'basename' filename; do .... done < <(find "$1" -type f -print0) é segura contra todos os nomes de arquivos.

basename não é mais usado porque estamos vendo arquivos em subdiretórios e precisamos manter os subdiretórios. Portanto, no lugar da chamada para fn=${filename#$1} , a linha filename é usada. Isso apenas remove de $1 o prefixo que contém o diretório a/b/c/some.txt .

Problema 2

Suponha que correspondamos os arquivos por nome, mas independentemente do diretório. Em outras palavras, se o primeiro diretório contiver um arquivo some.txt , nós o consideraremos presente no segundo diretório se o arquivo %code% existir em qualquer subdiretório do segundo diretório. Para fazer isso, substitua o loop acima por:

%pre%     
por John1024 25.05.2014 / 22:38
3

O FSlint é um pequeno aplicativo GUI que ajuda você a identificar e limpar seu sistema de arquivos redundantes.

Instalando o FSlint

Instale o FSlint no Ubuntu Software Center ou na linha de comando da seguinte forma:

sudo apt-get install fslint

(No meu sistema, a instalação do FSlint não trouxe dependências adicionais. Especificamente, o fslint depende do findutils, python e python-glade2, que devem estar todos no seu sistema. Você pode remover o FSlint usando o Centro de Software ou digitando sudo apt-get autoremove --purge fslint em um terminal).

Pesquisando arquivos

Inicie o FSlint no Unity Dash.

Aqui está uma captura de tela da tela principal. Existem muitos recursos avançados, mas o uso básico do aplicativo é relativamente simples.

Clique no botão Add no canto superior esquerdo para adicionar todos os diretórios que deseja verificar. Obviamente, você pode remover diretórios usando o botão Remove .

Verifiqueseacaixadeseleçãorecurse?àdireitaestáselecionada.Emseguida,cliquenobotãoFind.(Quaisquererros,comoproblemasdepermissãodearquivo,serãoimpressosnaparteinferiordajaneladoFSlint).

OFSlintlistarátodososarquivosduplicados,seuslocaisdediretórioeadatadoarquivo.OFSlinttambémapresentaonúmerodebytesperdidosdevidoaosarquivosredundantes.

Remoçãodeduplicados

AgoravocêpodeselecionarváriosarquivosusandoasteclasShiftouCtrleobotãoesquerdodomouse.Sevocêquiserselecionarváriosarquivosautomaticamente,cliquenobotãoSelectereceberáopçõescomoaseleçãodearquivoscombasenadataouainserçãodecritériosdeseleçãodecuringas.

SevocêprecisarusaralistadearquivosselecionadosforadoFSlint(talvezcomoentradaparaseupróprioscript),cliquenobotãoSaveparasalvarumarquivodetexto.

Porfim,vocêpodeexcluirosarquivosselecionadosusandoobotãoDeleteoupodemesclarosarquivosselecionadosusandoobotãoMerge.ObservequeorecursoMergeremoveosarquivosnãoselecionadosdeseusistemaecrialinksfísicosparaosarquivosselecionadoscorrespondentes.Vocêusariaesserecursosequisessemantersuaestruturadearquivosexistente,masquisesseliberaralgumespaçoemseusistema.

Recursos adicionais & Documentação

O FSlint possui outros recursos poderosos que podem ser acessados nas guias no painel esquerdo. Eu achei Name clashes para ser útil onde existem arquivos que têm o mesmo nome, mas são diferentes (talvez porque você salvou uma versão mais nova de um arquivo em um diretório diferente).

Há também uma guia Advanced search parameters na parte superior da janela do FSlint que permite excluir determinados diretórios em sua pesquisa ou filtrar seus resultados usando parâmetros.

Há muitos recursos poderosos nessa pequena ferramenta simples. Pode poupar o esforço de ter que escrever e depurar um script. Você pode descobrir mais no link . Aqui está um link direto para o guia em inglês: link .

    
por PJ Singh 26.05.2014 / 07:29
1

Eu quero compartilhar isso porque acho que é muito divertido.

Para cada subpasta abaixo da pasta de destino, geramos um hash para essa subpasta.

Cada hash de pasta é gerado a partir do resultado de todos os arquivos abaixo dessa pasta. Portanto, qualquer pasta contendo arquivos idênticos na mesma estrutura deve produzir o mesmo hash!

No final, usamos uniq para exibir somente os hashes de pastas duplicadas.

Salve o script a seguir como seek_duplicate_folders.sh e, em seguida, execute-o da seguinte forma:

$ bash seek_duplicate_folders.sh [root_folder_to_scan]

Aqui está o script:

#!/bin/bash
target="$1"

hash_folder() {
  echo "Hashing $1" >/dev/stderr
  pushd "$1" >/dev/null

  # Hash all the files
  find . -type f | sort | xargs md5sum |

  # Hash that list of hashes, discard the newline character,
  # and append the folder name
  md5sum - | tr -d '\n'
  printf "  %s\n" "$1"

  popd >/dev/null
}

find "$target" -type d |
while read dir
do hash_folder "$dir"
done |
sort |
# Display only the lines with duplicate hashes (first 32 chars are duplicates)
uniq -D -w 32

Advertências:

  • Ineficiente: arquivos md5sums no fundo da árvore várias vezes (uma vez por pasta ancestral)
  • Não detecta diferenças em registros de data e hora, propriedade ou permissões de arquivos
  • Ignora pastas vazias. Portanto, duas pastas que contêm arquivos idênticos, ou nenhum arquivo, mas têm pastas vazias diferentes, ainda serão relatadas como idênticas.
  • Ignora links simbólicos. Arquivos contendo diferentes links simbólicos ainda podem ser relatados como idênticos.
por joeytwiddle 10.12.2015 / 04:45