Como encontro mais de 200K arquivos com nomes diferentes e saída

2

Temos uma SAN com muito (mais de 5 milhões) de arquivos. Por causa de algum erro estranho de um script de desenvolvedor, alguns arquivos foram apagados e agora temos que descobrir quais arquivos foram excluídos.

Agora, eu tenho uma lista com os nomes dos arquivos (do nosso banco de dados) que precisam ser verificados, o que é assim:

49
50
51
52

Eu descobri como posso usar o comando find para fazer isso em arquivos únicos e múltiplos, da seguinte maneira:

find /mnt/SAN/documents/ -type f -name"92441_1"

Isso funciona, mas não posso usar esse comando com mais de 200K + arquivos para verificar. Então, eu estou querendo saber se alguém sabe uma maneira de obter o comando find para receber entrada do meu arquivo, ou se houver outro comando que eu possa usar para fazer isso.

    
por Sefiris 31.05.2016 / 15:40

4 respostas

2

Eu faria algo como (assumindo as ferramentas do GNU):

find /mnt/SAN/documents -type f -print0 | awk -F / '
  NR == FNR{check[$0]; next}
  $NF in check {print "found:", $0; delete check[$NF]}
  END {
    for (i in check)
      print "Not found:", i
  }' filename.list RS='
find /mnt/SAN/documents -type f -print0 | awk -F / '
  NR == FNR{check[$0]; notfound[$0]; next}
  $NF in check {print "found:", $0; delete notfound[$NF]}
  END {
    for (i in notfound)
      print "Not found:", i
  }' filename.list RS='
find /mnt/SAN/documents -type f -print0 | awk -F / '
  NR == FNR{check[$0]; next}
  $NF in check {print "found:", $0; delete check[$NF]}
  END {
    for (i in check)
      print "Not found:", i
  }' filename.list RS='
find /mnt/SAN/documents -type f -print0 | awk -F / '
  NR == FNR{check[$0]; notfound[$0]; next}
  $NF in check {print "found:", $0; delete notfound[$NF]}
  END {
    for (i in notfound)
      print "Not found:", i
  }' filename.list RS='%pre%' -
' -
' -
' -

Qual seria a ocorrência de uma ocorrência para cada nome de arquivo em filename.list .

Ou para denunciar todas as ocorrências:

%pre%     
por 31.05.2016 / 16:05
1

Use algo como

find /mnt/SAN/documents/ -type f | perl -ple 's,^.*/,,' > files_currently_present

para gerar uma lista de arquivos atualmente em disco, sem caminhos e, em seguida, usar

comm -2 -3 filelist_from database files_currently_present

para compará-lo à lista do seu backup e gerar uma lista de arquivos mssing.

    
por 31.05.2016 / 17:55
0

A maneira mais simples é ler os nomes dos arquivos usando um loop de shell e, em seguida, executar vários comandos find em segundo plano:

while IFS= read -r file; do
    find /mnt/SAN/documents/ -type f -name "$file" &
done < fileList.txt > foundFiles.txt

Isso, no entanto, lançará mais de 200 mil instâncias de find e provavelmente deixará sua máquina de joelhos. Uma abordagem melhor seria criar um comando find complexo, fornecendo a cada nome de arquivo:

$ printf 'find /mnt/SAN/documents/ -type f '; while IFS= read -r file; do printf -- '-name "%s" -o ' "$file"; done < fileList.txt | sed 's/-o $/\n/'
find /mnt/SAN/documents/ -type f -name "49" -o -name "50" -o -name "51" -o -name "52" 

Você pode então executar o próprio comando copiando / colando-o ou com:

eval $(printf 'find /mnt/SAN/documents/ -type f '; \
    while IFS= read -r file; do 
        printf -- '-name "%s" -o ' "$file"; done < fileList.txt | 
            sed 's/-o $/\n/')

No entanto, isso também será interrompido se você tiver muitos arquivos, portanto, será necessário executá-lo em lotes:

for i in $(seq 1 100 $(wc -l < fileList.txt)); do 
    k=$((i+100)); 
    printf 'find /mnt/SAN/documents/ -type f '; 
    sed -n "$i,${k}p" fileList.txt | 
    while IFS= read -r file; do 
        printf -- '-name "%s" -o ' "$file"; 
    done  | sed 's/-o $/\n/';   
done

Isso criará comandos find separados para cada lote de 100 arquivos em sua lista, que você pode executar com eval , como mostrado acima, ou apenas salvar em um arquivo e executar o arquivo:

for i in $(seq 1 100 $(wc -l < fileList.txt)); do 
    k=$((i+100)); 
    printf 'find /mnt/SAN/documents/ -type f '; 
    sed -n "$i,${k}p" fileList.txt | 
    while IFS= read -r file; do 
        printf -- '-name "%s" -o ' "$file"; 
    done  | sed 's/-o $/\n/';   
done > script.sh && bash script.sh > foundFiles.txt

Note que a abordagem de Stéphane , começando com arquivos existentes e verificando quais estão faltando, certamente seria melhor aqui ( a menos que os arquivos existentes sejam mais do que os arquivos ausentes). Em uma veia semelhante, você poderia primeiro criar uma lista de todos os arquivos existentes e então usar comm para compará-los à lista de arquivos de destino (já que você diz que tem uma lista de arquivos, assumirei que seus nomes nunca contém caracteres de nova linha):

find /mnt/SAN/documents/ -type f | sort > found
comm -13 <(sort found) <(sort fileList.txt)

O comando comm imprimirá todas as linhas que estão em fileList.txt , mas não em found .

    
por 31.05.2016 / 16:20
-1

É muito demorado executar o encontro em cada nome de arquivo em arquivos de 200K +. Se eu fosse você, correria find ${FILESROOT} > /tmp/SANfiles then

for filename in $(cat my_database_files)
do
  grep "${filename}" /tmp/SANfiles > /dev/null; r=${?}
  if [ ${r} -eq 0 ]
  then
    echo ${filename} >> /tmp/existing_files
  else
    echo ${filename} >> /tmp/missing_files
  fi
done

dependendo de como os nomes de arquivo em seu banco de dados estão formatados, você pode querer modificar o nome da variável no loop for, mas acho que você tem a essência da minha ideia.

    
por 31.05.2016 / 15:48