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? ¿Una forma en la que puedo pasar el nombre del archivo y el árbol de directorios para buscar?
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? ¿Una forma en la que puedo pasar el nombre del archivo y el árbol de directorios para buscar?
Respuestas:
os.walk es la respuesta, esta encontrará la primera coincidencia:
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 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')
if name in file or name in dirs
for name in files:fallará al buscar super-photo.jpgcuando esté super-photo.JPGen el sistema de archivos. (una hora de mi vida que me gustaría recuperar ;-) La solución un poco desordenada esif str.lower(name) in [x.lower() for x in files]
Usé una versión de os.walky en un directorio más grande, obtuve tiempos de alrededor de 3,5 segundos. Probé dos soluciones aleatorias sin gran mejora, luego lo hice:
paths = [line[2:] for line in subprocess.check_output("find . -iname '*.txt'", shell=True).splitlines()]
Si bien es solo POSIX, obtuve 0.25 seg.
A partir de esto, creo que es totalmente posible optimizar mucho la búsqueda completa de una manera independiente de la plataforma, pero aquí es donde detuve la investigación.
Si está usando Python en Ubuntu y solo quiere que funcione en Ubuntu de una manera sustancialmente más rápida, use el locateprograma del 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_resultses una listde las rutas de archivo absolutas. Esto es 10,000 veces más rápido que los métodos anteriores y para una búsqueda que hice fue ~ 72,000 veces más rápido.
En Python 3.4 o más reciente, puede usar pathlib para hacer globbing recursivo:
>>> import pathlib
>>> sorted(pathlib.Path('.').glob('**/*.py'))
[PosixPath('build/lib/pathlib.py'),
PosixPath('docs/conf.py'),
PosixPath('pathlib.py'),
PosixPath('setup.py'),
PosixPath('test_pathlib.py')]
Referencia: https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob
En Python 3.5 o más reciente, también puede hacer globbing recursivo como este:
>>> import glob
>>> glob.glob('**/*.txt', recursive=True)
['2.txt', 'sub/3.txt']
Referencia: https://docs.python.org/3/library/glob.html#glob.glob
Para una búsqueda rápida e independiente del sistema operativo, utilice scandir
https://github.com/benhoyt/scandir/#readme
Lea http://bugs.python.org/issue11406 para obtener detalles sobre el motivo.
Si está trabajando con Python 2, tiene un problema con la recursividad infinita en las ventanas causada por enlaces simbólicos autorreferentes.
Este script evitará seguir esos. 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 todas las rutas que apuntan a archivos en la lista de nombres de archivo. Uso:
find("C:\\", ["file1.abc", "file2.abc", "file3.abc", "file4.abc", "file5.abc"])
A continuación, usamos un argumento booleano "primer" para cambiar entre la primera coincidencia y todas las coincidencias (un valor predeterminado que es equivalente a "buscar archivo de nombre"):
import os
def find(root, file, first=False):
for d, subD, f in os.walk(root):
if file in f:
print("{0} : {1}".format(file, d))
if first == True:
break
La respuesta es muy similar a las existentes, pero ligeramente optimizada.
Para que pueda encontrar cualquier archivo o carpeta por patrón:
def iter_all(pattern, path):
return (
os.path.join(root, entry)
for root, dirs, files in os.walk(path)
for entry in dirs + files
if pattern.match(entry)
)
ya sea por subcadena:
def iter_all(substring, path):
return (
os.path.join(root, entry)
for root, dirs, files in os.walk(path)
for entry in dirs + files
if substring in entry
)
o usando un predicado:
def iter_all(predicate, path):
return (
os.path.join(root, entry)
for root, dirs, files in os.walk(path)
for entry in dirs + files
if predicate(entry)
)
para buscar solo archivos o solo carpetas, reemplace "dirs + archivos", por ejemplo, con solo "dirs" o solo "archivos", según lo que necesite.
Saludos.
La respuesta de SARose funcionó para mí hasta que actualicé desde Ubuntu 20.04 LTS. El pequeño cambio que hice en su código lo hace funcionar en la última versión de Ubuntu.
import subprocess
def find_files(file_name):
file_name = 'chromedriver'
command = ['locate'+ ' ' + file_name]
output = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True).communicate()[0]
output = output.decode()
search_results = output.split('\n')
return search_results