У мене є файл, який може знаходитися в іншому місці на машині кожного користувача. Чи є спосіб здійснити пошук файлу? Як я можу передати ім'я файлу та дерево каталогів для пошуку?
У мене є файл, який може знаходитися в іншому місці на машині кожного користувача. Чи є спосіб здійснити пошук файлу? Як я можу передати ім'я файлу та дерево каталогів для пошуку?
Відповіді:
os.walk - це відповідь, це знайде першу відповідність:
import os
def find(name, path):
for root, dirs, files in os.walk(path):
if name in files:
return os.path.join(root, name)
І це знайде всі збіги:
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
І це буде відповідати шаблону:
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:не вдасться шукати, super-photo.jpgколи він super-photo.JPGзнаходиться у файловій системі. (годину мого життя я хотів би повернути ;-) Дещо безладний виправленняif str.lower(name) in [x.lower() for x in files]
Я використовував версію os.walkі в більшому каталозі отримав час близько 3,5 сек. Я спробував два випадкових рішення, не маючи великих вдосконалень, тоді просто зробив:
paths = [line[2:] for line in subprocess.check_output("find . -iname '*.txt'", shell=True).splitlines()]
Хоча це лише POSIX, я отримав 0,25 сек.
З цього я вважаю, що цілком можливо оптимізувати весь пошук багато незалежно від платформи, але саме на цьому я зупинив дослідження.
Якщо ви використовуєте Python в Ubuntu, і ви хочете, щоб він працював на Ubuntu значно більш швидким способом - це використання програми терміналу, locateяк це.
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- це listабсолютний шлях до файлу. Це в 10000 разів швидше, ніж описані вище методи, і для одного пошуку я зробив це ~ 72 000 разів швидше.
У Python 3.4 або новіших версіях ви можете використовувати pathlib для рекурсивного глобулювання:
>>> 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')]
Довідка: https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob
У Python 3.5 або новіших версіях ви також можете робити рекурсивний глобулінг таким чином:
>>> import glob
>>> glob.glob('**/*.txt', recursive=True)
['2.txt', 'sub/3.txt']
Довідка: https://docs.python.org/3/library/glob.html#glob.glob
Для швидкого пошуку, незалежного від ОС, використовуйте scandir
https://github.com/benhoyt/scandir/#readme
Прочитайте http://bugs.python.org/issue11406, щоб дізнатись чому.
Якщо ви працюєте з Python 2, у вас є проблема з нескінченною рекурсією на windows, спричиненою самостійними посиланнями.
Цей сценарій дозволить уникнути слідування цим. Зауважте, що це специфічно для 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
Він повертає список із усіма шляхами, які вказують на файли у списку імен файлів. Використання:
find("C:\\", ["file1.abc", "file2.abc", "file3.abc", "file4.abc", "file5.abc"])
Нижче ми використовуємо булевий "перший" аргумент для перемикання між першим і всім збігами (за замовчуванням, який еквівалентний "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
Відповідь дуже схожа на існуючі, але трохи оптимізована.
Таким чином, ви можете знайти будь-які файли чи папки за зразком:
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)
)
або за підстрочкою:
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
)
або за допомогою присудка:
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)
)
шукати лише файли або лише папки - замініть "dirs + файли", наприклад, "dirs" або лише "файли", залежно від того, що вам потрібно.
З повагою
Відповідь SARose працював на мене, поки я не оновив з Ubuntu 20.04 LTS. Незначна зміна його коду змушує його працювати над останньою версією 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