Znajdź plik w Pythonie


110

Mam plik, który może znajdować się w innym miejscu na komputerze każdego użytkownika. Czy istnieje sposób na zaimplementowanie wyszukiwania pliku? W jaki sposób mogę przekazać nazwę pliku i drzewo katalogów do przeszukania?


Zobacz moduł systemu operacyjnego dla os.walk lub os.listdir Zobacz również to pytanie stackoverflow.com/questions/229186/ ... po przykładowy kod
Martin Beckett

Odpowiedzi:


251

os.walk jest odpowiedzią, to znajdzie pierwsze dopasowanie:

import os

def find(name, path):
    for root, dirs, files in os.walk(path):
        if name in files:
            return os.path.join(root, name)

A to znajdzie wszystkie dopasowania:

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

A to będzie pasować do wzorca:

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
Zauważ, że te przykłady pozwolą znaleźć tylko pliki, a nie katalogi o tej samej nazwie. Jeśli chcesz znaleźć dowolny obiekt w katalogu o tej nazwie, którego możesz użyćif name in file or name in dirs
Mark E. Hamilton,

9
Uważaj na wielkość liter. for name in files:nie uda się wyszukać, super-photo.jpgkiedy znajduje się super-photo.JPGw systemie plików. (chciałbym z powrotem godzinę mojego życia ;-) Trochę if str.lower(name) in [x.lower() for x in files]
bałaganu

A co z użyciem wydajności zamiast tworzenia listy wyników? ..... if fnmatch.fnmatch (nazwa, wzorzec): yield os.path.join (root, name)
Berci

Proszę rozważyć aktualizację swojej odpowiedzi do prymitywów Pythona 3.x
Dima Tisnek

1
Lista Comprehention może zastąpić funkcję, np. Find_all: res = [os.path.join (root, name) dla root, dirs, files in os.walk (path) if name in files]
Nir

23

Użyłem wersji os.walki na większym katalogu uzyskałem czasy około 3,5 sekundy. Wypróbowałem dwa przypadkowe rozwiązania bez dużej poprawy, a potem po prostu:

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

Chociaż jest to tylko POSIX, mam 0,25 sek.

Na tej podstawie uważam, że jest całkowicie możliwe zoptymalizowanie całego wyszukiwania w sposób niezależny od platformy, ale w tym miejscu zatrzymałem badania.


6

Jeśli używasz Pythona na Ubuntu i chcesz, aby działał tylko na Ubuntu, znacznie szybszym sposobem jest użycie takiego locateprogramu terminala .

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_resultsjest jedną listz bezwzględnych ścieżek plików. Jest to 10 000 razy szybciej niż metody powyżej, a dla jednego wyszukiwania, które przeprowadziłem, było ~ 72 000 razy szybciej.


5

W Pythonie 3.4 lub nowszym możesz użyć pathlib do rekurencyjnego globbingu:

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

Źródła: https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob

W Pythonie 3.5 lub nowszym możesz również wykonywać rekurencyjne globbowanie w następujący sposób:

>>> import glob
>>> glob.glob('**/*.txt', recursive=True)
['2.txt', 'sub/3.txt']

Źródła: https://docs.python.org/3/library/glob.html#glob.glob


3

Aby uzyskać szybkie wyszukiwanie niezależne od systemu operacyjnego, użyj scandir

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

Przeczytaj http://bugs.python.org/issue11406, aby dowiedzieć się, dlaczego.


7
W szczególności użyj odpowiedzi scandir.walk()według @ Nadii. Zauważ, że jeśli używasz Pythona 3.5+, os.walk()ma scandir.walk()już przyspieszenia. Ponadto PEP 471 jest prawdopodobnie lepszym dokumentem do przeczytania w celu uzyskania informacji niż ten problem.
Ben Hoyt

3

Jeśli pracujesz z Pythonem 2, masz problem z nieskończoną rekurencją w oknach, spowodowaną przez odsyłacze symboliczne.

Ten skrypt będzie unikać ich przestrzegania. Zwróć uwagę, że dotyczy to systemu 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

Zwraca listę ze wszystkimi ścieżkami, które wskazują pliki na liście nazw plików. Stosowanie:

find("C:\\", ["file1.abc", "file2.abc", "file3.abc", "file4.abc", "file5.abc"])

2

Poniżej używamy logicznego „pierwszego” argumentu do przełączania między pierwszym dopasowaniem a wszystkimi dopasowaniami (wartość domyślna jest równoważna z „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

Odpowiedź jest bardzo podobna do istniejących, ale nieco zoptymalizowana.

Możesz więc znaleźć dowolne pliki lub foldery według wzorca:

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

albo przez podciąg:

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
    )

lub używając predykatu:

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

aby przeszukać tylko pliki lub tylko foldery - zamień „dirs + files”, na przykład, na tylko „dirs” lub tylko „files”, w zależności od tego, czego potrzebujesz.

Pozdrowienia.


0

Odpowiedź SARose działała dla mnie, dopóki nie zaktualizowałem Ubuntu 20.04 LTS. Niewielka zmiana, którą wprowadziłem w jego kodzie, sprawia, że ​​działa on w najnowszej wersji 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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.