2009-11-12 18 views
60

Tengo un archivo que puede estar en un lugar diferente en la máquina de cada usuario. ¿Hay alguna forma de implementar una búsqueda del archivo? ¿De qué manera puedo pasar el nombre del archivo y el árbol del directorio para buscar?Buscar un archivo en python

Respuesta

146

os.walk es la respuesta, esta se encuentra el primer partido:

import os 

def find(name, path): 
    for root, dirs, files in os.walk(path): 
     if name in files: 
      return os.path.join(root, name) 

Y esto encontrará todas las coincidencias:

def find_all(name, path): 
    result = [] 
    for root, dirs, files in os.walk(path): 
     if name in files: 
      result.append(os.path.join(root, name)) 
    return result 

Y esto va a coincidir con un patrón:

import os, fnmatch 
def find(pattern, path): 
    result = [] 
    for root, dirs, files in os.walk(path): 
     for name in files: 
      if fnmatch.fnmatch(name, pattern): 
       result.append(os.path.join(root, name)) 
    return result 

find('*.txt', '/path/to/dir') 
+1

Tenga en cuenta que estos ejemplos solo encontrarán archivos, no directorios con el mismo nombre. Si quiere encontrar ** cualquier objeto ** en el directorio con ese nombre, puede usar 'if name in file o name in dirs' –

+3

Tenga cuidado con la distinción entre mayúsculas y minúsculas. 'para nombre en archivos:' fallará buscando 'super-photo.jpg' cuando es' super-photo.JPG' en el sistema de archivos. (una hora de mi vida me gustaría volver ;-) Una solución algo desordenada es 'if str.lower (name) en [x.lower() para x en archivos] ' –

+0

¿Qué hay de usar ** yield ** en lugar de preparar la lista de resultados? ..... if fnmatch.fnmatch (nombre, patrón): yield os.path.join (root, name) – Berci

2

Para una búsqueda rápida, independiente del sistema operativo, utilice scandir

https://github.com/benhoyt/scandir/#readme

Leer http://bugs.python.org/issue11406 para los detalles de por qué.

+2

Específicamente, use 'scandir.walk() 'por la respuesta de Nadia. Tenga en cuenta que si usa Python 3.5+, 'os.walk()' ya tiene las aceleraciones 'scandir.walk()'. Además, [PEP 471] (https://www.python.org/dev/peps/pep-0471/) es probablemente un documento mejor para leer para obtener información que ese problema. –

13

Usé una versión de os.walk y en un directorio más grande obtuve tiempos de aproximadamente 3,5 segundos. Probé dos soluciones al azar sin gran mejora, entonces acabo de hacer:

paths = [line[2:] for line in subprocess.check_output("find . -iname '*.txt'", shell=True).splitlines()] 

Si bien es POSIX-solamente, tengo 0,25 seg.

De esto, creo que es completamente posible optimizar toda la búsqueda de una manera independiente de la plataforma, pero aquí es donde detuve la investigación.

0

Si está utilizando Python en Ubuntu y solo quiere que funcione en Ubuntu, una forma sustancialmente más rápida es usar el programa locate de la terminal de esta manera.

import subprocess 

def find_files(file_name): 
    command = ['locate', file_name] 

    output = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0] 
    output = output.decode() 

    search_results = output.split('\n') 

    return search_results 

search_results es una list de las rutas de archivos absolutas. Esto es 10.000 veces más rápido que los métodos anteriores y en una búsqueda que hice fue ~ 72,000 veces más rápido.

1

Si está trabajando con Python 2, tiene un problema con la recursión infinita en Windows causada por enlaces simbólicos autorreferenciales.

Esta secuencia de comandos evitará seguirlos. Tenga en cuenta que esto es específico de Windows!

import os 
from scandir import scandir 
import ctypes 

def is_sym_link(path): 
    # http://stackoverflow.com/a/35915819 
    FILE_ATTRIBUTE_REPARSE_POINT = 0x0400 
    return os.path.isdir(path) and (ctypes.windll.kernel32.GetFileAttributesW(unicode(path)) & FILE_ATTRIBUTE_REPARSE_POINT) 

def find(base, filenames): 
    hits = [] 

    def find_in_dir_subdir(direc): 
     content = scandir(direc) 
     for entry in content: 
      if entry.name in filenames: 
       hits.append(os.path.join(direc, entry.name)) 

      elif entry.is_dir() and not is_sym_link(os.path.join(direc, entry.name)): 
       try: 
        find_in_dir_subdir(os.path.join(direc, entry.name)) 
       except UnicodeDecodeError: 
        print "Could not resolve " + os.path.join(direc, entry.name) 
        continue 

    if not os.path.exists(base): 
     return 
    else: 
     find_in_dir_subdir(base) 

    return hits 

devuelve una lista con todos los caminos que apuntan a archivos de la lista de nombres de archivo. Uso:

find("C:\\", ["file1.abc", "file2.abc", "file3.abc", "file4.abc", "file5.abc"]) 
+0

Tenga en cuenta que esto es específico de Windows – Leliel

+0

@Leliel Lo he agregado a la respuesta. Gracias por tus comentarios. –

Cuestiones relacionadas