Obtenga una lista filtrada de archivos en un directorio


281

Estoy tratando de obtener una lista de archivos en un directorio usando Python, pero no quiero una lista de TODOS los archivos.

Lo que esencialmente quiero es la capacidad de hacer algo como lo siguiente, pero usando Python y no ejecutando ls.

ls 145592*.jpg

Si no hay un método incorporado para esto, actualmente estoy pensando en escribir un bucle for para iterar a través de los resultados de un os.listdir() y agregar todos los archivos coincidentes a una nueva lista.

Sin embargo, hay muchos archivos en ese directorio y, por lo tanto, espero que haya un método más eficiente (o un método incorporado).


[Este enlace podría ayudarlo :) Obtenga una lista filtrada de archivos en un directorio] ( codereview.stackexchange.com/a/33642 )
sha111

Tenga en cuenta que puede tener especial cuidado con el orden de clasificación si esto es importante para su aplicación.
lumbric

Respuestas:


385

21
Oh, acabo de notar que los documentos de Python dicen que glob () "se hace usando las funciones os.listdir () y fnmatch.fnmatch () en concierto, y no invocando una subshell". En otras palabras, glob () no tiene las mejoras de eficiencia que uno podría esperar.
Ben Hoyt

55
Hay una diferencia principal: glob.glob('145592*.jpg')imprime toda la ruta absoluta de los archivos mientras ls 145592*.jpgimprime solo la lista de archivos.
Ébe Isaac

8
@Ben ¿Por qué invocar una subshell (subproceso) tiene mejoras de eficiencia?
Paulo Neves

77
@PauloNeves: cierto, mi comentario anterior tampoco tiene sentido para mí 7 años después. :-) Supongo que me refería al hecho de que glob()solo usa listdir + fnmatch, en lugar de llamadas especiales del sistema operativo para hacer el filtrado de comodines. Por ejemplo, en Windows, la FindFirstFileAPI le permite especificar comodines para que el sistema operativo realice el filtrado directamente, y presumiblemente de manera más eficiente (no creo que haya un equivalente en Linux).
Ben Hoyt

1
@marsh: Como siempre, el directorio de trabajo actual del proceso.
Ignacio Vazquez-Abrams

125

glob.glob()Definitivamente es la forma de hacerlo (según Ignacio). Sin embargo, si necesita una correspondencia más complicada, puede hacerlo con una comprensión de la lista y re.match(), algo así:

files = [f for f in os.listdir('.') if re.match(r'[0-9]+.*\.jpg', f)]

Más flexible, pero como notas, menos eficiente.


Esto definitivamente parece ser más poderoso. Por ejemplo, tener que hacer algo como[0-9]+
demongolem

3
Sí, definitivamente más potente, sin embargo, fnmatch admite [0123456789]secuencias ( ver documentos ), y también tiene la fnmatch.filter()función que hace que este bucle sea un poco más eficiente.
Ben Hoyt

49

Mantenlo simple:

import os
relevant_path = "[path to folder]"
included_extensions = ['jpg','jpeg', 'bmp', 'png', 'gif']
file_names = [fn for fn in os.listdir(relevant_path)
              if any(fn.endswith(ext) for ext in included_extensions)]

Prefiero esta forma de comprensión de listas porque se lee bien en inglés.

Leí la cuarta línea como: Por cada fn en os.listdir para mi ruta, dame solo las que coincidan con cualquiera de mis extensiones incluidas.

Puede ser difícil para los programadores principiantes de Python acostumbrarse realmente a usar las comprensiones de listas para el filtrado, y puede tener cierta sobrecarga de memoria para conjuntos de datos muy grandes, pero para enumerar un directorio y otras tareas simples de filtrado de cadenas, las comprensiones de listas conducen a una mayor limpieza código documentable

Lo único de este diseño es que no lo protege contra el error de pasar una cadena en lugar de una lista. Por ejemplo, si accidentalmente convierte una cadena en una lista y termina comprobando todos los caracteres de una cadena, podría terminar obteniendo una serie de falsos positivos.

Pero es mejor tener un problema fácil de solucionar que una solución difícil de entender.


55
No es que sea necesario any()aquí, porque str.endswith()toma una secuencia de finales. if fn.endswith(included_extentensions)Es más que suficiente.
Martijn Pieters

3
Además de la ineficiencia de no usar str.endswith(seq)eso que Martijn señaló, esto no es correcto, porque un archivo tiene que terminar .extpara que tenga esa extensión. Este código también encontrará (por ejemplo) un archivo llamado "myjpg" o un directorio llamado simplemente "png". Para solucionarlo, solo prefija cada extensión included_extensionscon a ..
Ben Hoyt

Siempre desconfío un poco del código en las respuestas que obviamente no se han ejecutado o no se pueden ejecutar. La variable included_extensionsvs included_extentsions? Una pena porque de lo contrario esta es mi respuesta preferida.
Auspicio


17

Filtrar con globmódulo:

Importar glob

import glob

Comodines:

files=glob.glob("data/*")
print(files)

Out:

['data/ks_10000_0', 'data/ks_1000_0', 'data/ks_100_0', 'data/ks_100_1',
'data/ks_100_2', 'data/ks_106_0', 'data/ks_19_0', 'data/ks_200_0', 'data/ks_200_1', 
'data/ks_300_0', 'data/ks_30_0', 'data/ks_400_0', 'data/ks_40_0', 'data/ks_45_0', 
'data/ks_4_0', 'data/ks_500_0', 'data/ks_50_0', 'data/ks_50_1', 'data/ks_60_0', 
'data/ks_82_0', 'data/ks_lecture_dp_1', 'data/ks_lecture_dp_2']

Extensión de fiter .txt:

files = glob.glob("/home/ach/*/*.txt")

Un solo personaje

glob.glob("/home/ach/file?.txt")

Rangos de números

glob.glob("/home/ach/*[0-9]*")

Rangos del alfabeto

glob.glob("/home/ach/[a-c]*")

12

Código preliminar

import glob
import fnmatch
import pathlib
import os

pattern = '*.py'
path = '.'

Solución 1 - use "glob"

# lookup in current dir
glob.glob(pattern)

In [2]: glob.glob(pattern)
Out[2]: ['wsgi.py', 'manage.py', 'tasks.py']

Solución 2 - use "os" + "fnmatch"

Variante 2.1 - Búsqueda en el directorio actual

# lookup in current dir
fnmatch.filter(os.listdir(path), pattern)

In [3]: fnmatch.filter(os.listdir(path), pattern)
Out[3]: ['wsgi.py', 'manage.py', 'tasks.py']

Variante 2.2 - Búsqueda recursiva

# lookup recursive
for dirpath, dirnames, filenames in os.walk(path):

    if not filenames:
        continue

    pythonic_files = fnmatch.filter(filenames, pattern)
    if pythonic_files:
        for file in pythonic_files:
            print('{}/{}'.format(dirpath, file))

Resultado

./wsgi.py
./manage.py
./tasks.py
./temp/temp.py
./apps/diaries/urls.py
./apps/diaries/signals.py
./apps/diaries/actions.py
./apps/diaries/querysets.py
./apps/library/tests/test_forms.py
./apps/library/migrations/0001_initial.py
./apps/polls/views.py
./apps/polls/formsets.py
./apps/polls/reports.py
./apps/polls/admin.py

Solución 3 - use "pathlib"

# lookup in current dir
path_ = pathlib.Path('.')
tuple(path_.glob(pattern))

# lookup recursive
tuple(path_.rglob(pattern))

Notas:

  1. Probado en Python 3.4
  2. El módulo "pathlib" se agregó solo en Python 3.4
  3. Python 3.5 agregó una función para la búsqueda recursiva con glob.glob https://docs.python.org/3.5/library/glob.html#glob.glob . Como mi máquina está instalada con Python 3.4, no lo he probado.

9

use os.walk para enumerar recursivamente sus archivos

import os
root = "/home"
pattern = "145992"
alist_filter = ['jpg','bmp','png','gif'] 
path=os.path.join(root,"mydir_to_scan")
for r,d,f in os.walk(path):
    for file in f:
        if file[-3:] in alist_filter and pattern in file:
            print os.path.join(root,file)

No hay necesidad de cortar; file.endswith(alist_filter)es suficiente.
Martijn Pieters

5
import os

dir="/path/to/dir"
[x[0]+"/"+f for x in os.walk(dir) for f in x[2] if f.endswith(".jpg")]

Esto le dará una lista de archivos jpg con su ruta completa. Puede reemplazar x[0]+"/"+fcon fsolo nombres de archivo. También puede reemplazar f.endswith(".jpg")con cualquier condición de cadena que desee.


3

También es posible que desee un enfoque de más alto nivel (lo he implementado y empaquetado como findtools ):

from findtools.find_files import (find_files, Match)


# Recursively find all *.txt files in **/home/**
txt_files_pattern = Match(filetype='f', name='*.txt')
found_files = find_files(path='/home', match=txt_files_pattern)

for found_file in found_files:
    print found_file

se puede instalar con

pip install findtools

2

Nombres de archivo con extensiones "jpg" y "png" en "ruta / a / imágenes":

import os
accepted_extensions = ["jpg", "png"]
filenames = [fn for fn in os.listdir("path/to/images") if fn.split(".")[-1] in accepted_extensions]

Esto es muy similar a la respuesta dada por @ ramsey0
chb

1

Puede usar pathlib que está disponible en la biblioteca estándar Python 3.4 y superior.

from pathlib import Path

files = [f for f in Path.cwd().iterdir() if f.match("145592*.jpg")]

1

Puede definir un patrón y verificarlo. Aquí he tomado tanto el patrón inicial como el final y los busqué en el nombre del archivo. ARCHIVOS contiene la lista de todos los archivos en un directorio.

import os
PATTERN_START = "145592"
PATTERN_END = ".jpg"
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
for r,d,FILES in os.walk(CURRENT_DIR):
    for FILE in FILES:
        if PATTERN_START in FILE and PATTERN_END in FILE:
            print FILE

0

¿Qué tal str.split ()? Nada que importar.

import os

image_names = [f for f in os.listdir(path) if len(f.split('.jpg')) == 2]

2
Esto es muy similar a la respuesta dada
Sushanth

Esto parece ser similar a la respuesta de @ ramsey0 usando f.endswith('.jpg')(pero también seleccionará filename.jpg.ext)
anjsimmo

-1

Puede usar subprocess.check_ouput () como

import subprocess

list_files = subprocess.check_output("ls 145992*.jpg", shell=True) 

Por supuesto, la cadena entre comillas puede ser cualquier cosa que desee ejecutar en el shell y almacenar la salida.


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.