Suchen Sie eine Datei in Python

109

Ich habe eine Datei, die sich möglicherweise an einer anderen Stelle auf dem Computer jedes Benutzers befindet. Gibt es eine Möglichkeit, eine Suche nach der Datei durchzuführen? Eine Möglichkeit, den Dateinamen und den Verzeichnisbaum für die Suche zu übergeben?

Regie
quelle
Siehe das OS- Modul für os.walk oder os.listdir. Siehe auch diese Frage stackoverflow.com/questions/229186/… für Beispielcode
Martin Beckett

Antworten:

249

os.walk ist die Antwort, dies wird die erste Übereinstimmung finden:

import os

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

Und das findet alle Übereinstimmungen:

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

Und das passt zu einem Muster:

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')
Nadia Alramli
quelle
2
Beachten Sie, dass diese Beispiele nur Dateien finden, keine Verzeichnisse mit demselben Namen. Wenn Sie ein Objekt im Verzeichnis mit diesem Namen suchen möchten, das Sie möglicherweise verwenden möchtenif name in file or name in dirs
Mark E. Hamilton,
9
Achten Sie auf die Groß- und Kleinschreibung. for name in files:wird nicht suchen, super-photo.jpgwenn es super-photo.JPGim Dateisystem ist. (eine Stunde meines Lebens würde ich gerne zurück ;-) Etwas chaotisch istif str.lower(name) in [x.lower() for x in files]
matt wilkie
Was ist mit der Verwendung von Yield anstelle der Erstellung der Ergebnisliste? ..... wenn fnmatch.fnmatch (Name, Muster): Ausbeute os.path.join (Wurzel, Name)
Berci
Bitte erwägen Sie, Ihre Antwort auf Python 3.x-
Grundelemente
1
Die Verständnisliste kann die Funktion ersetzen, z. B. find_all: res = [os.path.join (root, name) für root, dirs, Dateien in os.walk (Pfad), wenn Name in Dateien]
Nir
23

Ich habe eine Version von os.walkund in einem größeren Verzeichnis mal ca. 3,5 sek bekommen. Ich habe zwei zufällige Lösungen ohne große Verbesserung ausprobiert und dann einfach:

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

Während es nur POSIX ist, habe ich 0,25 Sekunden.

Aus diesem Grund glaube ich, dass es durchaus möglich ist, die gesamte Suche plattformunabhängig zu optimieren, aber hier habe ich die Forschung eingestellt.

kgadek
quelle
6

Wenn Sie Python unter Ubuntu verwenden und nur möchten, dass es unter Ubuntu wesentlich schneller funktioniert, verwenden Sie das locateProgramm des Terminals wie folgt .

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_resultsist einer listder absoluten Dateipfade. Dies ist 10.000 Mal schneller als die oben genannten Methoden und für eine Suche, die ich durchgeführt habe, war es ~ 72.000 Mal schneller.

SARose
quelle
5

In Python 3.4 oder neuer können Sie pathlib verwenden, um rekursives Globbing durchzuführen:

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

Referenz: https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob

In Python 3.5 oder neuer können Sie auch rekursives Globbing wie folgt ausführen:

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

Referenz: https://docs.python.org/3/library/glob.html#glob.glob

Kenyon
quelle
3

Verwenden Sie für eine schnelle, betriebssystemunabhängige Suche scandir

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

Lesen Sie http://bugs.python.org/issue11406, um zu erfahren, warum.

Dima Tisnek
quelle
7
Verwenden Sie insbesondere scandir.walk()die Antwort von @ Nadia. Beachten Sie, dass bei Verwendung von Python 3.5+ os.walk()die scandir.walk()Beschleunigung bereits vorhanden ist. Außerdem ist PEP 471 wahrscheinlich ein besseres Dokument zum Lesen als dieses Problem.
Ben Hoyt
3

Wenn Sie mit Python 2 arbeiten, haben Sie ein Problem mit der unendlichen Rekursion unter Windows, das durch selbstreferenzierende Symlinks verursacht wird.

Dieses Skript vermeidet es, diesen zu folgen. Beachten Sie, dass dies Windows-spezifisch ist !

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

Es wird eine Liste mit allen Pfaden zurückgegeben, die auf Dateien in der Dateinamenliste verweisen. Verwendung:

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

Im Folgenden verwenden wir ein boolesches Argument "first", um zwischen der ersten Übereinstimmung und allen Übereinstimmungen zu wechseln (eine Standardeinstellung, die "find. -Name file" entspricht):

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 
Leon Chang
quelle
0

Die Antwort ist den bestehenden sehr ähnlich, aber leicht optimiert.

So können Sie alle Dateien oder Ordner nach Muster finden:

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

entweder durch Teilzeichenfolge:

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
    )

oder mit einem Prädikat:

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

Um nur Dateien oder nur Ordner zu durchsuchen, ersetzen Sie "dirs + files" beispielsweise durch "dirs" oder nur "files", je nachdem, was Sie benötigen.

Grüße.

Stanislav Kuzmich
quelle