在 python 中查找文件

我有一个文件,可能在每个用户的机器上不同的地方。有没有实现文件搜索的方法?一种方法,我可以传递文件的名称和目录树搜索?

389192 次浏览

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

我使用了一个版本的 os.walk,并在一个更大的目录得到的时间约3.5秒。我尝试了两个随机的解决方案,没有很大的改善,然后就这样做了:

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

虽然它是 POSIX-only,我有0.25秒。

由此,我相信完全有可能以一种独立于平台的方式对整个搜索进行大量优化,但这就是我停止研究的地方。

如果你在 Ubuntu 上使用 Python,并且你只想让它在 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。这比上面的方法快了10,000倍,对于我做过的一次搜索,它快了约72,000倍。

如果您使用的是 Python2,那么由于自引用符号链接导致的窗口上的无限递归就会出现问题。

此脚本将避免遵循这些。请注意,这是 特定窗口

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

在 Python 3.4或更新的版本中,您可以使用 pathlib 来执行递归 globbing:

>>> 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或更新的版本中,你也可以像下面这样做递归 globbing:

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

参考资料: https://docs.python.org/3/library/glob.html#glob.glob

下面我们使用一个布尔型的“ first”参数在第一个匹配和所有匹配之间切换(默认值相当于“ find”)。- 名称档案”) :

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

答案与现有的非常相似,但稍微优化了一些。

所以你可以通过模式找到任何文件或文件夹:

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”,这取决于您需要什么。

问候。

在我从 Ubuntu 20.04 LTS 升级之前,SARose 的回答对我很有用。我对他的代码做了一点小小的改动,使得它能够在最新的 Ubuntu 版本中工作。

import subprocess


def find_files(file_name):
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

@ F. M.F 的答案在这个版本中有一些问题,所以我做了一些调整以使它工作。

import os
from os 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(str(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
except PermissionError:
print("Skipped " + os.path.join(direc, entry.name) + ". I lacked permission to navigate")
continue


if not os.path.exists(base):
return
else:
find_in_dir_subdir(base)


return hits

Unicode ()在 Python3中被更改为 str () ,因此我进行了调整(第8行)

我还在 PermisonError 中添加了(第25行)和异常。这样,程序就不会在找到无法访问的文件夹时停止。

最后,我想给一个小小的警告。在运行该程序时,即使您正在查找单个文件/目录,也要确保将其作为列表传递。否则,您将得到许多与搜索不一定匹配的答案。

使用范例:

Find (“ C:”,[“ Python”,“ Homework”])

或者

查找(“ C:”,[“家庭作业”])

但是,例如: find (“ C:”,“家庭作业”)会给出不需要的答案。

如果我说我知道为什么会这样,那是在撒谎。再说一次,这不是我的代码,我只是做了一些必要的调整来使其正常工作。所有的功劳都应该归@F。M.F.