Знайдіть файл у python


110

У мене є файл, який може знаходитися в іншому місці на машині кожного користувача. Чи є спосіб здійснити пошук файлу? Як я можу передати ім'я файлу та дерево каталогів для пошуку?


Дивіться модуль os для os.walk або os.listdir Дивіться також це питання stackoverflow.com/questions/229186/… для зразкового коду
Martin Beckett

Відповіді:


251

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')

2
Зауважте, що в цих прикладах можна знайти лише файли, а не каталоги з тим самим іменем. Якщо ви хочете знайти будь-який об’єкт у каталозі з таким ім'ям, яке ви можете використовуватиif name in file or name in dirs
Марк Е. Гамільтон

9
Будьте уважні до чутливості справи. for name in files:не вдасться шукати, super-photo.jpgколи він super-photo.JPGзнаходиться у файловій системі. (годину мого життя я хотів би повернути ;-) Дещо безладний виправленняif str.lower(name) in [x.lower() for x in files]
matt wilkie

Що з використанням урожайності замість підготовки списку результатів? ..... якщо fnmatch.fnmatch (ім'я, візерунок): урожай os.path.join (корінь, ім'я)
Berci

Будь ласка, подумайте над оновленням своєї відповіді до примітивів Python 3.x
Діма Тиснек

1
Список Comprehention може замінити функцію, наприклад, find_all: res = [os.path.join (корінь, ім'я) для кореня, dirs, файли в os.walk (шлях), якщо ім'я у файлах]
Nir,

23

Я використовував версію os.walkі в більшому каталозі отримав час близько 3,5 сек. Я спробував два випадкових рішення, не маючи великих вдосконалень, тоді просто зробив:

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

Хоча це лише POSIX, я отримав 0,25 сек.

З цього я вважаю, що цілком можливо оптимізувати весь пошук багато незалежно від платформи, але саме на цьому я зупинив дослідження.


6

Якщо ви використовуєте 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 разів швидше.


5

У 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


3

Для швидкого пошуку, незалежного від ОС, використовуйте scandir

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

Прочитайте http://bugs.python.org/issue11406, щоб дізнатись чому.


7
Зокрема, скористайтеся відповіддю scandir.walk()на відповідь @ Nadia. Зверніть увагу , що якщо ви використовуєте Python 3.5+, os.walk()має scandir.walk()прискорень вже. Крім того, PEP 471 - це, мабуть, кращий документ для читання для інформації, ніж це питання.
Бен Хойт

3

Якщо ви працюєте з 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"])

2

Нижче ми використовуємо булевий "перший" аргумент для перемикання між першим і всім збігами (за замовчуванням, який еквівалентний "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 

0

Відповідь дуже схожа на існуючі, але трохи оптимізована.

Таким чином, ви можете знайти будь-які файли чи папки за зразком:

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" або лише "файли", залежно від того, що вам потрібно.

З повагою


0

Відповідь 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.