Python, cómo implementar algo como .gitignore comportamiento

Necesito enumerar todos los archivos en el directorio actual (.) (Incluyendo todos los subdirectorios), y excluir algunos archivos como funciona .gitignore ( http://git-scm.com/docs/gitignore )

Con fnmatch ( https://docs.python.org/2/library/fnmatch.html ) podré “filtrar” archivos usando un patrón

ignore_files = ['*.jpg', 'foo/', 'bar/hello*'] matches = [] for root, dirnames, filenames in os.walk('.'): for filename in fnmatch.filter(filenames, '*'): matches.append(os.path.join(root, filename)) 

¿Cómo puedo “filtrar” y obtener todos los archivos que no coincidan con uno o más elementos de mis “ignore_files”?

¡Gracias!

    Está en el camino correcto: si desea utilizar patrones de estilo fnmatch , debe usar fnmatch.filter con ellos.

    Pero hay tres problemas que hacen que esto no sea tan trivial.

    En primer lugar, desea aplicar múltiples filtros. ¿Cómo haces eso? Llame al filter varias veces:

     for ignore in ignore_files: filenames = fnmatch.filter(filenames, ignore) 

    En segundo lugar, realmente desea hacer lo contrario del filter : devolver el subconjunto de nombres que no coinciden. Como explica la documentación:

    Es lo mismo que [n for n in names if fnmatch(n, pattern)] , pero implementado de manera más eficiente.

    Entonces, para hacer lo opuesto, solo tiras un not :

     for ignore in ignore_files: filenames = [n for n in filenames if not fnmatch(n, ignore)] 

    Finalmente, está intentando filtrar las rutas de acceso parciales, no solo los nombres de archivo, sino que no está realizando la join hasta después del filtrado. Así que cambia el orden:

     filenames = [os.path.join(root, filename) for filename in filenames] for ignore in ignore_files: filenames = [n for n in filenames if not fnmatch(n, ignore)] matches.extend(filenames) 

    Hay algunas maneras en que podría mejorar esto.

    Es posible que desee utilizar una expresión generadora en lugar de una comprensión de lista (paréntesis en lugar de corchetes), por lo tanto, si tiene listas enormes de nombres de archivos, está utilizando una tubería perezosa en lugar de perder tiempo y espacio en repetidas ocasiones la creación de listas enormes.

    Además, puede o no ser más fácil de entender si invierte el orden de los bucles, de esta manera:

     filenames = (n for n in filenames if not any(fnmatch(n, ignore) for ignore in ignore_files)) 

    Finalmente, si le preocupa el rendimiento, puede usar fnmatch.translate en cada expresión para convertirlas en expresiones regulares equivalentes, luego fusionarlas en una gran expresión regular y comstackrla, y usarla en lugar de un bucle alrededor de fnmatch . Esto puede complicarse si se permite que sus patrones sean más complicados que solo *.jpg , y no lo recomendaría a menos que realmente identifique un cuello de botella en el rendimiento aquí. Pero si necesita hacerlo, he visto al menos una pregunta en SO donde alguien hizo un gran esfuerzo para resolver todos los casos de vanguardia, así que busque en lugar de intentar escribirlo usted mismo.

     matches.extend([fn for fn if not filename in ignore_files]) 

    Debería hacer el truco para nombres de archivos simples, para ignorar patrones como:

     def reject(filename, filter): """ Takes a filename and a filter to reject files that match.""" if len(filter)==0: return False else: return fnmatch.fnmach(filename, filter[0]) or reject(filename, filter[1:]) matches.extend([os.path.join(root, fn) for fn in filenames if not reject(fn, ignore_files)]) 

    Mientras se crea una lista a partir de los nombres de archivo en os.walk, se comprobará que ninguno de los filtros proporcione una coincidencia; los filtros se verifican hasta que no quede ninguno o se encuentre la primera coincidencia, por lo que debería ser bastante rápido.

    También puedes probar algo como:

     filenames = set(filenames) # convert to a set for filter in ignore_files: filenames = filenames - set(fnmatch.filter(filenames, filter)) # remove the matches matches.extend([os.path.join(root, fn) for fn in filenames]) # Add to matches