Ho un file che potrebbe trovarsi in una posizione diversa sulla macchina di ciascun utente. C'è un modo per implementare una ricerca per il file? Un modo in cui posso passare il nome del file e l'albero delle directory in cui cercare?
Ho un file che potrebbe trovarsi in una posizione diversa sulla macchina di ciascun utente. C'è un modo per implementare una ricerca per il file? Un modo in cui posso passare il nome del file e l'albero delle directory in cui cercare?
Risposte:
os.walk è la risposta, questa troverà la prima corrispondenza:
import os
def find(name, path):
for root, dirs, files in os.walk(path):
if name in files:
return os.path.join(root, name)
E questo troverà tutte le corrispondenze:
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
E questo corrisponderà a uno schema:
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:fallirà la ricerca super-photo.jpgquando è super-photo.JPGnel file system. (Un'ora della mia vita mi piacerebbe indietro ;-) Un po 'di soluzione disordinata èif str.lower(name) in [x.lower() for x in files]
Ho usato una versione di os.walke su una directory più grande ho avuto tempi di circa 3,5 sec. Ho provato due soluzioni casuali senza grandi miglioramenti, poi l'ho fatto:
paths = [line[2:] for line in subprocess.check_output("find . -iname '*.txt'", shell=True).splitlines()]
Sebbene sia solo POSIX, ho ottenuto 0,25 sec.
Da questo, credo che sia del tutto possibile ottimizzare molto la ricerca completa in modo indipendente dalla piattaforma, ma è qui che ho interrotto la ricerca.
Se stai usando Python su Ubuntu e vuoi che funzioni solo su Ubuntu, un modo sostanzialmente più veloce è usare il locateprogramma del terminale come questo.
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è uno listdei percorsi di file assoluti. Questo è 10.000 volte più veloce dei metodi sopra e per una ricerca che ho fatto è stato ~ 72.000 volte più veloce.
In Python 3.4 o versioni successive puoi usare pathlib per eseguire il globbing ricorsivo:
>>> 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')]
Riferimento: https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob
In Python 3.5 o versioni successive puoi anche eseguire il globbing ricorsivo in questo modo:
>>> import glob
>>> glob.glob('**/*.txt', recursive=True)
['2.txt', 'sub/3.txt']
Riferimento: https://docs.python.org/3/library/glob.html#glob.glob
Per una ricerca rapida e indipendente dal sistema operativo, utilizzare scandir
https://github.com/benhoyt/scandir/#readme
Leggi http://bugs.python.org/issue11406 per i dettagli sul perché.
Se stai lavorando con Python 2 hai un problema con la ricorsione infinita su Windows causata da collegamenti simbolici autoreferenziali.
Questo script eviterà di seguirli. Nota che questo è specifico di 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
Restituisce un elenco con tutti i percorsi che puntano ai file nell'elenco dei nomi di file. Uso:
find("C:\\", ["file1.abc", "file2.abc", "file3.abc", "file4.abc", "file5.abc"])
Di seguito usiamo un argomento booleano "first" per passare dalla prima corrispondenza a tutte le corrispondenze (un valore predefinito che è equivalente a "find. -Name file"):
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 risposta è molto simile a quelle esistenti, ma leggermente ottimizzata.
Quindi puoi trovare qualsiasi file o cartella in base al modello:
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)
)
sia per sottostringa:
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 utilizzando un predicato:
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)
)
per cercare solo file o solo cartelle - sostituire "dirs + files", ad esempio, con solo "dirs" o solo "file", a seconda di ciò che ti serve.
Saluti.
La risposta di SARose ha funzionato per me fino a quando non ho aggiornato da Ubuntu 20.04 LTS. La leggera modifica che ho apportato al suo codice lo fa funzionare sull'ultima versione di 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