Tenho um arquivo que pode estar em um local diferente na máquina de cada usuário. Existe uma maneira de implementar uma pesquisa para o arquivo? Uma forma de passar o nome do arquivo e a árvore de diretórios a pesquisar?
Tenho um arquivo que pode estar em um local diferente na máquina de cada usuário. Existe uma maneira de implementar uma pesquisa para o arquivo? Uma forma de passar o nome do arquivo e a árvore de diretórios a pesquisar?
Respostas:
os.walk é a resposta, ele encontrará a primeira correspondência:
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 isso encontrará todas as correspondências:
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 isso vai corresponder a um padrão:
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:falhará ao procurar super-photo.jpgquando está super-photo.JPGno sistema de arquivos. (uma hora da minha vida que eu gostaria de voltar ;-) Solução um pouco complicada éif str.lower(name) in [x.lower() for x in files]
Usei uma versão de os.walke em um diretório maior, com tempos em torno de 3,5 segundos. Tentei duas soluções aleatórias sem grandes melhorias, mas fiz:
paths = [line[2:] for line in subprocess.check_output("find . -iname '*.txt'", shell=True).splitlines()]
Embora seja apenas POSIX, eu tenho 0,25 seg.
A partir disso, acredito que seja inteiramente possível otimizar bastante a busca inteira de forma independente da plataforma, mas foi aí que parei a pesquisa.
Se você está usando Python no Ubuntu e deseja que ele funcione apenas no Ubuntu, uma forma substancialmente mais rápida é usar o locateprograma do terminal desta forma.
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é um listdos caminhos de arquivo absolutos. Isso é 10.000 vezes mais rápido do que os métodos acima e, para uma pesquisa que fiz, foi aproximadamente 72.000 vezes mais rápido.
No Python 3.4 ou mais recente, você pode usar pathlib para fazer 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')]
Referência: https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob
No Python 3.5 ou mais recente, você também pode fazer globbing recursivo como este:
>>> import glob
>>> glob.glob('**/*.txt', recursive=True)
['2.txt', 'sub/3.txt']
Referência: https://docs.python.org/3/library/glob.html#glob.glob
Para uma pesquisa rápida e independente do sistema operacional, use scandir
https://github.com/benhoyt/scandir/#readme
Leia http://bugs.python.org/issue11406 para obter detalhes sobre o motivo.
Se você estiver trabalhando com Python 2, terá um problema com a recursão infinita em janelas causada por links simbólicos autorreferidos.
Este script evitará seguir aqueles. Observe que isso é específico do 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
Ele retorna uma lista com todos os caminhos que apontam para arquivos na lista de nomes de arquivos. Uso:
find("C:\\", ["file1.abc", "file2.abc", "file3.abc", "file4.abc", "file5.abc"])
A seguir, usamos um argumento booleano "primeiro" para alternar entre a primeira correspondência e todas as correspondências (um padrão que é 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
A resposta é muito semelhante às existentes, mas ligeiramente otimizada.
Assim, você pode encontrar qualquer arquivo ou pasta por padrão:
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)
)
quer por substring:
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
)
ou usando um 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 pesquisar apenas arquivos ou apenas pastas - substitua “dirs + arquivos”, por exemplo, por apenas “dirs” ou apenas “arquivos”, dependendo do que você precisa.
Saudações.
A resposta de SARose funcionou para mim até que eu atualizei do Ubuntu 20.04 LTS. A ligeira mudança que fiz em seu código o faz funcionar na versão mais recente do 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