Script para excluir arquivos que não correspondem a determinados nomes de arquivos

2

Eu tenho uma pasta com 500.000 imagens, classificadas em subpastas por ano e mês. Gostaria de criar um script que faça isso:

Se o nome do arquivo não corresponder a nenhum dos nomes de arquivo em names.log , exclua o arquivo. names.log conteria nomes de arquivos, como:

image1.jpg
photo3.jpg
redcar.jpg
balloon2323.jpg

etc ... tem cerca de 10.000 nomes de arquivos que eu quero manter

Eu tenho PHP e Python no servidor, mas não tenho certeza do que seria melhor para isso. Eu não fiz nenhum script antes. Alguém poderia ser tão gentil para me dar um trecho de código que iria conseguir isso e deixe-me saber como executá-lo? Ou talvez isso possa ser conseguido com um comando?

    
por location 22.02.2015 / 03:55

4 respostas

3

Isso é bastante fácil em Python com os.walk . Aviso, código não testado. Eu suponho que a lista de nomes contém um nome por linha com

#!/usr/bin/python2
import os
names_file = open('names.log')
names = set(line.rstrip('\n') for line in names_file.readlines())
names_file.close()
for root, dirs, files in os.walk('/path/to/top/directory'):
    for name in files:
        path = os.path.join(root, name)
        if os.path.isfile(path):
            if name not in names:
                print path
                #os.remove(path) # uncomment this line if you're happy with the set of files to remove
    
por 22.02.2015 / 17:40
1
find -name '*.jpg' -print0 | grep -zZ -vf name.log | xargs -0 COMMAND

substitua COMMAND por ls -l e, se gostar, rm

Editar : O comando apresentado é tratar name.log como um conjunto de expressões regulares. @terdon lembrou-se de tha name.log é uma lista de nomes de arquivos.

Se nomes de arquivos usam os caracteres "normais" usuais, isso provavelmente seria suficiente, mas problemas podem ocorrer se:

  1. Nomes de arquivo / regexp incluem caracteres incomuns como [ , ] , etc. (nesse caso, pode não ser possível excluir alguns arquivos e até excluir alguns arquivos cujo nome está em name.log !). Para evitar isso, podemos usar grep -F ou proteger o caractere especial em name.log .
  2. Regexp corresponde a uma subseqüência de nomes de arquivos (nesse caso, alguns arquivos não seriam excluídos - a.jpg corresponderia a todas as imagens terminadas com "a" como camera.jpg , banana.jpg ).

Para o caso 2, para a situação de prefixos, - podemos adicionar "/" no início do regexp.

sed 's!^!/!'  name.log > new.log
find -name '*.jpg' -print0 | grep -F -zZ -vf new.log | xargs -0 COMMAND

ou até mesmo

find -name '*.jpg' -print0 | grep -zZFvf <(sed 's!^!/!' name.log) | xargs COMMAND

para o caso 2, a situação do sufixo é menos importante porque os arquivos de imagem possuem extensões. Para resolver este caso corretamente, precisamos dizer que "não há nada após o nome do arquivo": precisamos de regexp e caracteres especiais (exemplo . [ ] ) em nomes de arquivos precisam ser protegidos.

sed -re 's!([].[])!\!g; s!.*!/&$!' name.log > new.log
find -name '*.jpg' -print0 | grep -zZ -vf new.log | xargs -0 COMMAND
    
por 24.02.2015 / 12:25
0

Isso é realmente fácil com pax . Ele tem uma noção de uma opção -s ubstitution que pode alterar nomes de arquivos à medida que são gravados. Você também pode especificar mais de um argumento -s ubstitution. E, mais relevante aqui, é que os membros selecionados têm apenas tantos argumentos -s ubstitution aplicados quanto necessário para fazer uma correspondência com sucesso, mas qualquer substituição que resulte em um nome de arquivo nulo faz com que o arquivo correspondente não seja selecionado.

Para demonstrar:

mkdir test; cd test
touch match nomatch
pax -ws '|^.*/match$|&|' -s '|.*||' ./ |
pax -v

O acima faz e se transforma em um diretório ./test , cria dois arquivos e, em seguida, -w rites um tar archive em um canal com pax cujo conteúdo um segundo pax -v erbosely listas. As impressões acima:

-rw-r--r-- 1 mikeserv mikeserv 0 Feb 22 11:40 ./

... porque ./match é correspondido antes da subtituição final, que substitui todos os caracteres em qualquer nome de arquivo.

E com pax você realmente não precisa copiar o conteúdo de um arquivo para o seu arquivo - você pode usar -rwl , que é uma operação de cópia que cria hardlinks.

Então, se o seu arquivo foi nomeado paxscript e parecia ...

cd -- "$1"
pax -rwvl \
    -s '|^.*/image1\.jpg$|&|' \
    -s '|^.*/photo3\.jpg$|&|' \
    -s '|^.*/redcar\.jpg$|&|' \
    -s '|^.*/balloon2323\.jpg$|&|' \
    -s '|.*||' ./ ../"${1##*/}.mirror"
cd - >/dev/null

... e então você executou como ...

. ./paxscript "$targetdir"

Ele criaria um espelho de "$targetdir" em seu parentdir contendo apenas hardlinks para os nomes de arquivos que você deseja combinar. Você pode então verificar se os resultados estão do seu agrado antes de fazer rm -rf "$targetdir" e se livrar de apenas todos os nomes de caminho que você não deseja.

por 22.02.2015 / 21:54
-1

Se você satisfizer com bash , gostaria de oferecer o seguinte algoritmo (pode ser realizado em qualquer linguagem de script):

  1. Criar lista de arquivos presentes: find /path_to_folder -name "*.jpg" -fprint files.tmp
  2. Classifique files.tmp e name.log do que compare por comm -23 files.tmp name.log
  3. Passe a lista de arquivos exclusivos para files.tmp to rm command

Esteja ciente do caminho dos arquivos - em files.tmp e name.log , pode ser o mesmo (completo ou relativo a uma pasta). O separador de nome de arquivo para name.log no caso seria newline .

    
por 22.02.2015 / 11:56