Pythonでファイルを見つける


110

各ユーザーのマシンの別の場所にあるファイルがあります。ファイルの検索を実装する方法はありますか?ファイルの名前とディレクトリツリーを渡して検索する方法は?


os.walkまたはos.listdir のosモジュールを参照してください。サンプルコードについては、この質問stackoverflow.com/questions/229186/…も参照してください
マーティンベケット

回答:


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
Mark E. Hamilton

9
大文字と小文字の区別に注意してください。ファイルシステムにある場合、検索にfor name in files:失敗します。(私の人生の1時間、私は戻ってきたいです;-)やや厄介な修正はsuper-photo.jpgsuper-photo.JPGif str.lower(name) in [x.lower() for x in files]
マットウィルキー14

結果リストを準備する代わりに、収量を使用するのはどうですか?..... if fnmatch.fnmatch(name、pattern):yield os.path.join(root、name)
Berci

Python 3.xプリミティブへの回答の更新を検討してください
Dima Tisnek '19

1
たとえば、find_all:res = [os.path.join(root、name)for root、dirs、files in os.walk(path)if name in files]
Nir

23

のバージョンを使用したos.walkところ、より大きなディレクトリで3.5秒ほど時間がかかりました。私は2つのランダムなソリューションを試しましたが、大きな改善はありませんでした。

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

POSIXのみですが、0.25秒かかりました。

このことから、プラットフォームに依存しない方法で検索全体を大幅に最適化することは完全に可能であると私は信じていますが、ここで私は研究をやめました。


6

UbuntuでPythonを使用していて、UbuntuでのみPythonを動作させたい場合は、端末の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_resultslist絶対ファイルパスのです。これは上記の方法よりも10,000倍高速であり、1回の検索で約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

OSに依存しない高速検索には、 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はおそらく、その問題よりも情報を読むための優れたドキュメントです。
Ben Hoyt

3

Python 2を使用している場合、自己参照シンボリックリンクが原因でウィンドウが無限に再帰するという問題があります。

このスクリプトは、それらをフォローすることを避けます。これはウィンドウ固有のものであることに注意してください!

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 + files」を「dirs」のみまたは「files」のみに置き換えます。

よろしく。


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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.