Temukan file dengan python

110

Saya memiliki file yang mungkin berada di tempat yang berbeda pada mesin setiap pengguna. Apakah ada cara untuk mengimplementasikan pencarian file? Sebuah cara agar saya dapat mengirimkan nama file dan pohon direktori untuk mencari?

arahan
sumber
Lihat modul os untuk os.walk atau os.listdir Lihat juga pertanyaan ini stackoverflow.com/questions/229186/… untuk kode contoh
Martin Beckett

Jawaban:

251

os.walk adalah jawabannya, ini akan menemukan pertandingan pertama:

import os

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

Dan ini akan menemukan semua kecocokan:

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

Dan ini akan cocok dengan pola:

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
sumber
2
Perhatikan bahwa contoh ini hanya akan menemukan file, bukan direktori dengan nama yang sama. Jika Anda ingin menemukan objek apa pun dalam direktori dengan nama itu, Anda mungkin ingin menggunakanif name in file or name in dirs
Mark E. Hamilton
9
Hati-hati dengan sensitivitas huruf. for name in files:akan gagal mencari super-photo.jpgsaat berada super-photo.JPGdi sistem file. (satu jam dalam hidup saya, saya ingin kembali ;-) Perbaikan yang agak berantakan adalahif str.lower(name) in [x.lower() for x in files]
matt wilkie
Bagaimana dengan menggunakan hasil daripada menyiapkan daftar hasil? ..... if fnmatch.fnmatch (name, pattern): menghasilkan os.path.join (root, name)
Berci
Harap pertimbangkan untuk memperbarui jawaban Anda ke primitif Python 3.x
Dima Tisnek
1
Daftar pemahaman dapat menggantikan fungsi, misalnya find_all: res = [os.path.join (root, name) untuk root, dirs, file di os.walk (path) jika nama dalam file]
Nir
23

Saya menggunakan versi os.walkdan pada direktori yang lebih besar mendapat waktu sekitar 3,5 detik. Saya mencoba dua solusi acak tanpa peningkatan yang berarti, lalu baru saja melakukannya:

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

Meskipun hanya untuk POSIX, saya mendapat 0,25 detik.

Dari sini, saya yakin sangat mungkin untuk banyak mengoptimalkan pencarian secara keseluruhan dengan cara yang tidak bergantung platform, tetapi di sinilah saya menghentikan penelitian.

kgadek
sumber
6

Jika Anda menggunakan Python di Ubuntu dan Anda hanya ingin Python bekerja di Ubuntu, cara yang jauh lebih cepat adalah menggunakan program terminal locateseperti ini.

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_resultsadalah salah listsatu jalur file absolut. Ini 10.000 kali lebih cepat daripada metode di atas dan untuk satu pencarian yang saya lakukan ~ 72.000 kali lebih cepat.

SARose
sumber
5

Di Python 3.4 atau yang lebih baru, Anda dapat menggunakan pathlib untuk melakukan globbing rekursif:

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

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

Di Python 3.5 atau yang lebih baru, Anda juga dapat melakukan globbing rekursif seperti ini:

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

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

Kenyon
sumber
3

Untuk pencarian cepat dan tidak tergantung OS, gunakan scandir

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

Baca http://bugs.python.org/issue11406 untuk detail alasannya.

Dima Tisnek
sumber
7
Secara khusus, gunakan scandir.walk()sesuai jawaban @ Nadia. Perhatikan bahwa jika Anda menggunakan Python 3.5+, os.walk()memiliki ekstensiscandir.walk() kecepatan. Juga, PEP 471 mungkin merupakan dokumen yang lebih baik untuk dibaca sebagai info daripada masalah itu.
Ben Hoyt
3

Jika Anda bekerja dengan Python 2, Anda memiliki masalah dengan rekursi tak terbatas pada windows yang disebabkan oleh symlink yang merujuk sendiri.

Skrip ini akan menghindari mengikuti itu. Perhatikan bahwa ini khusus untuk jendela !

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

Ini mengembalikan daftar dengan semua jalur yang mengarah ke file dalam daftar nama file. Pemakaian:

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

Di bawah ini kami menggunakan argumen boolean "pertama" untuk beralih antara kecocokan pertama dan semua kecocokan (default yang setara dengan "temukan. -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 
Leon Chang
sumber
0

Jawabannya sangat mirip dengan yang sudah ada, tetapi sedikit dioptimalkan.

Jadi Anda dapat menemukan file atau folder apa pun berdasarkan pola:

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

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

atau menggunakan predikat:

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

untuk mencari hanya file atau folder saja - ganti “dirs + files”, misalnya, dengan hanya “dirs” atau hanya “files”, tergantung pada apa yang Anda butuhkan.

Salam.

Stanislav Kuzmich
sumber
0

Jawaban SARose berfungsi untuk saya sampai saya memperbarui dari Ubuntu 20.04 LTS. Sedikit perubahan yang saya buat pada kodenya membuatnya berfungsi pada rilis Ubuntu terbaru.

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
Justin Turner
sumber