2009-10-10 18 views
36

He creado una galería en línea usando Python y Django. Empecé a agregar funcionalidad de edición, comenzando con una rotación. Yo uso sorl.thumbnail para autogenerar miniaturas a pedido.Eliminar varios archivos que coincidan con un patrón

Cuando edito el archivo original, necesito limpiar todas las miniaturas para que se generen nuevas. Hay tres o cuatro de ellos por imagen (tengo diferentes para diferentes ocasiones).

I podría código en el archivo-varients ... Pero eso es complicado y si cambio la forma en que hago las cosas, tendré que volver a visitar el código.

Idealmente me gustaría hacer una eliminación de expresiones regulares. En términos de expresiones regulares, todos mis originales se nombran así:

^(?P<photo_id>\d+)\.jpg$ 

Así que desea eliminar: (. ¿Dónde puedo reemplazar photo_id con el ID Quiero limpiar)

^(?P<photo_id>\d+)[^\d].*jpg$ 

Respuesta

37

Pruebe algo como esto:

import os, re 

def purge(dir, pattern): 
    for f in os.listdir(dir): 
     if re.search(pattern, f): 
      os.remove(os.path.join(dir, f)) 

Luego pasará el directorio que contiene los archivos y el patrón que desea combinar

+7

I Estaría tentado de compilar la expresión regular antes de entrar en el ciclo ... Pero sí, eso se ve bien. – Oli

+1

está bien usar 'glob.glob' en lugar de esto. – pylover

+0

Hermosa función. Gracias – ChickenFeet

8

Si necesita recursividad en varios subdirectorios, puede utilizar este método:

import os, re, os.path 
pattern = "^(?P<photo_id>\d+)[^\d].*jpg$" 
mypath = "Photos" 
for root, dirs, files in os.walk(mypath): 
    for file in filter(lambda x: re.match(pattern, x), files): 
     os.remove(os.path.join(root, file)) 

Puede quitar subdirectorios sobre la marcha desde dirs, que contiene la lista de los subdirectorios para visitar en cada nodo.

Tenga en cuenta que si se encuentra en un directorio, también puede obtener los archivos correspondientes a una expresión de patrón simple con glob.glob(pattern). En este caso, tendría que restar el conjunto de archivos para mantener el conjunto completo, por lo que el código anterior es más eficiente.

2

No es claro para mí que realmente desee hacer una coincidencia de grupos nombrados: en el uso que describe, el fotoid es entrada a la función de eliminación, y el propósito de los grupos nombrados es "salida", es decir, extraer ciertas subcadenas de la cadena coincidente (y acceder a ellas por su nombre en el objeto de coincidencia). Por lo tanto, yo recomendaría un enfoque más simple:

import re 
import os 

def delete_thumbnails(photoid, photodirroot): 
    matcher = re.compile(r'^%s\d+\D.*jpg$' % photoid) 
    numdeleted = 0 
    for rootdir, subdirs, filenames in os.walk(photodirroot): 
    for name in filenames: 
     if not matcher.match(name): 
     continue 
     path = os.path.join(rootdir, name) 
     os.remove(path) 
     numdeleted += 1 
    return "Deleted %d thumbnails for %r" % (numdeleted, photoid) 

Puede pasar el photoid como una cadena normal, o como una pieza del patrón RE si es necesario eliminar varios identificadores para apareamiento a la vez (por ejemplo, r'abc[def] para eliminar ABCD, abce y abcf en una sola llamada): esa es la razón por la que lo estoy insertando literalmente en el patrón RE, en lugar de insertar la cadena re.escape(photoid) como sería una práctica normal. Ciertas partes como contar el número de eliminaciones y devolver un mensaje informativo al final son, obviamente, adornos que debe eliminar si no le dan ningún valor agregado en su caso de uso.

Otros, como el patrón "if not ... // continue", son prácticas muy recomendadas en Python (el plano es mejor que el anidado: rescatar en el siguiente tramo del bucle tan pronto como determine que hay nada que hacer en este caso es mejor que anidar las acciones que se realizarán dentro de un if), aunque, por supuesto, otras disposiciones del código también funcionarían.

2

Mi recomendación:

def purge(dir, pattern, inclusive=True): 
    regexObj = re.compile(pattern) 
    for root, dirs, files in os.walk(dir, topdown=False): 
     for name in files: 
      path = os.path.join(root, name) 
      if bool(regexObj.search(path)) == bool(inclusive): 
       os.remove(path) 
     for name in dirs: 
      path = os.path.join(root, name) 
      if len(os.listdir(path)) == 0: 
       os.rmdir(path) 

Esto eliminará de forma recursiva todos los archivos que coincide con el patrón por defecto, y todos los archivos que no lo hace si incluido es cierto. A continuación, eliminará las carpetas vacías del árbol de directorios.

+0

Por favor, corrija la descripción de 'inclusive'. También sería mejor no hacer coincidir todo el camino con el patrón, sino solo el nombre del archivo, porque cuando el patrón está en otro lugar de la ruta, sin querer lo eliminará todo. También sería útil un comentario que el lenguaje de patrones sea el de 're' y no el de, por ejemplo, 'bash'. Sugeriré una edición respectiva. Es una cuestión de gusto si elimina los directorios vacíos de esa manera, ya que también eliminará los directorios vacíos que hayan estado vacíos incluso antes de que el patrón 'rm', es decir, por lo general, se elimine involuntariamente. – flaschbier

0

Encuentro que Popen(["rm " + file_name + "*.ext"], shell=True, stdout=PIPE).communicate() es una solución mucho más simple a este problema. Aunque esto es propenso a ataques de inyección, no veo ningún problema si su programa lo está usando internamente.

7

¿Qué le parece esto?

import glob, os, multiprocessing 
p = multiprocessing.Pool(4) 
p.map(os.remove, glob.glob("P*.jpg")) 

Ten en cuenta que esto no tiene recursividad y usa comodines (no regex).

+1

map() está destinado a transformar un conjunto de valores, en lugar de ejecutar un comando arbitrario sobre ellos. Por lo tanto, esta no es la técnica más recomendada, y en realidad falla en Python 3 ya que map() ahora devuelve un iterable, en lugar de evaluar inmediatamente las funciones. –

+2

Tenga en cuenta que el mapa devuelve un generador a partir de python 3 y esto * no * ejecutará la función eliminar. –

31

Una variación del enfoque pegote, que trabajará con Python 3:

import glob, os 
for f in glob.glob("P*.jpg"): 
    os.remove(f) 

Editar: En Python 3.4+ es posible que desee utilizar pathlib:

from pathlib import Path 
for p in Path(".").glob("P*.jpg"): 
    p.unlink() 
+0

os.remove me da un error del sistema (windows) así que usé shutil.rmtree en su lugar. – sparrow

+0

@sparrow La pregunta era cómo eliminar * archivos * que coincidan con un patrón, no para eliminar árboles de directorios completos que coincidan con un patrón. –

+0

Cambiar glob.glob ("P * .jpg") -> glob ("P * .jpg") – Patricio

-1
def recursive_purge(dir, pattern): 
    for f in os.listdir(dir): 
     if os.path.isdir(os.path.join(dir, f)): 
      recursive_purge(os.path.join(dir, f), pattern) 
     elif re.search(pattern, os.path.join(dir, f)): 
      os.remove(os.path.join(dir, f)) 
+1

Si bien este código puede resolver la pregunta, [incluyendo una explicación] (// meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) de cómo y por qué esto resuelve el problema realmente ayudaría a mejorar la calidad de su publicación, y probablemente resulte en más votos. Recuerde que está respondiendo la pregunta a los lectores en el futuro, no solo a la persona que pregunta ahora. Edite su respuesta para agregar una explicación y dé una indicación de qué limitaciones y suposiciones se aplican. – Makyen

Cuestiones relacionadas