获取目录中文件的过滤列表


281

我正在尝试使用Python获取目录中的文件列表,但是我不想要所有文件的列表。

我本质上想要的是能够执行以下操作但使用Python而不执行ls的功能。

ls 145592*.jpg

如果没有内置方法,我目前正在考虑编写一个for循环以遍历an的结果。 os.listdir()并将所有匹配的文件附加到新列表中。

但是,该目录中有很多文件,因此我希望有一种更有效的方法(或内置方法)。


[此链接可能会帮助您:)获取目录中的文件过滤列表](codereview.stackexchange.com/a/33642
sha111

请注意,如果这对您的应用程序很重要,则可能要特别注意排序顺序。
lumbric

Answers:


385

21
哦,我刚刚注意到Python文档说glob()“是通过同时使用os.listdir()和fnmatch.fnmatch()函数来完成的,而不是通过实际调用subshel​​l来完成的”。换句话说,glob()并没有让人期望的效率提高。
本·霍伊特

5
有一个主要区别:glob.glob('145592*.jpg')打印文件的整个绝对路径,而ls 145592*.jpg仅打印文件列表。
EBE艾萨克

8
@Ben为什么调用子外壳程序(子进程)会提高效率?
Paulo Neves

7
@PauloNeves:是的,7年后我的上述评论对我也没有意义。:-)我猜我指的是glob()只使用listdir + fnmatch而不是特殊的操作系统调用来进行通配符过滤的事实。例如,在Windows上,FindFirstFileAPI允许您指定通配符,因此操作系统可以直接进行过滤,并且可能更有效(我认为Linux上没有等效方法)。
Ben Hoyt

1
@marsh:和往常一样,该进程的当前工作目录。
伊格纳西奥·巴斯克斯

124

glob.glob()绝对是做到这一点的方式(根据Ignacio)。但是,如果您确实需要更复杂的匹配,则可以使用列表理解和来完成re.match(),例如:

files = [f for f in os.listdir('.') if re.match(r'[0-9]+.*\.jpg', f)]

更加灵活,但是您注意到效率更低。


这显然更强大。例如,必须做类似的事情[0-9]+
demongolem 2013年

3
是的,绝对更强大-但是fnmatch确实支持[0123456789]序列(请参阅docs),并且它还具有fnmatch.filter()使此循环稍微有效的功能。
Ben Hoyt

49

把事情简单化:

import os
relevant_path = "[path to folder]"
included_extensions = ['jpg','jpeg', 'bmp', 'png', 'gif']
file_names = [fn for fn in os.listdir(relevant_path)
              if any(fn.endswith(ext) for ext in included_extensions)]

我更喜欢这种形式的列表理解,因为它的英文读起来很好。

我将第四行读为:对于os.listdir中路径的每个fn,请仅提供与我包含的任何扩展名匹配的那些fn。

对于新手python程序员来说,可能很难真正习惯于使用列表推导进行过滤,并且对于非常大的数据集,它可能会有一些内存开销,但是对于列出目录和其他简单的字符串过滤任务,列表推导会导致更干净可记录的代码。

这种设计的唯一之处在于,它不能保护您避免犯错误,而不是传递字符串而不是列表。例如,如果您不小心将字符串转换为列表,并最终检查了字符串的所有字符,则可能最终会得到一系列误报。

但是,拥有一个易于解决的问题比解决一个难以理解的解决方案要好。


5
any()此处并不需要,因为str.endswith()需要一系列结尾。if fn.endswith(included_extentensions)绰绰有余。
马丁·彼得斯

3
除了不使用str.endswith(seq)Martijn指出的效率低下外,这是不正确的,因为文件必须以.ext该扩展名结尾。此代码还将找到(例如)名为“ myjpg”的文件或仅名为“ png”的目录。要解决此问题,只需在每个扩展名前included_extensions添加.
本·霍伊特,2016年

我总是对那些显然没有运行或无法运行的答案中的代码保持警惕。变量included_extensionsvs included_extentsions?可惜,因为否则这是我的首选答案。
奥义峰


17

过滤glob模块:

导入球

import glob

通配符:

files=glob.glob("data/*")
print(files)

Out:

['data/ks_10000_0', 'data/ks_1000_0', 'data/ks_100_0', 'data/ks_100_1',
'data/ks_100_2', 'data/ks_106_0', 'data/ks_19_0', 'data/ks_200_0', 'data/ks_200_1', 
'data/ks_300_0', 'data/ks_30_0', 'data/ks_400_0', 'data/ks_40_0', 'data/ks_45_0', 
'data/ks_4_0', 'data/ks_500_0', 'data/ks_50_0', 'data/ks_50_1', 'data/ks_60_0', 
'data/ks_82_0', 'data/ks_lecture_dp_1', 'data/ks_lecture_dp_2']

接头扩展.txt

files = glob.glob("/home/ach/*/*.txt")

一个字符

glob.glob("/home/ach/file?.txt")

编号范围

glob.glob("/home/ach/*[0-9]*")

字母范围

glob.glob("/home/ach/[a-c]*")

12

初步代码

import glob
import fnmatch
import pathlib
import os

pattern = '*.py'
path = '.'

解决方案1-使用“ glob”

# lookup in current dir
glob.glob(pattern)

In [2]: glob.glob(pattern)
Out[2]: ['wsgi.py', 'manage.py', 'tasks.py']

解决方案2-使用“操作系统” +“ fnmatch”

版本2.1-在当前目录中查找

# lookup in current dir
fnmatch.filter(os.listdir(path), pattern)

In [3]: fnmatch.filter(os.listdir(path), pattern)
Out[3]: ['wsgi.py', 'manage.py', 'tasks.py']

版本2.2-递归查找

# lookup recursive
for dirpath, dirnames, filenames in os.walk(path):

    if not filenames:
        continue

    pythonic_files = fnmatch.filter(filenames, pattern)
    if pythonic_files:
        for file in pythonic_files:
            print('{}/{}'.format(dirpath, file))

结果

./wsgi.py
./manage.py
./tasks.py
./temp/temp.py
./apps/diaries/urls.py
./apps/diaries/signals.py
./apps/diaries/actions.py
./apps/diaries/querysets.py
./apps/library/tests/test_forms.py
./apps/library/migrations/0001_initial.py
./apps/polls/views.py
./apps/polls/formsets.py
./apps/polls/reports.py
./apps/polls/admin.py

解决方案3使用“ pathlib”

# lookup in current dir
path_ = pathlib.Path('.')
tuple(path_.glob(pattern))

# lookup recursive
tuple(path_.rglob(pattern))

笔记:

  1. 在Python 3.4上测试
  2. 仅在Python 3.4中添加了模块“ pathlib”
  3. Python 3.5添加了glob.glob https://docs.python.org/3.5/library/glob.html#glob.glob递归查找的功能 。由于我的机器安装了Python 3.4,因此尚未进行测试。

9

使用os.walk递归列出您的文件

import os
root = "/home"
pattern = "145992"
alist_filter = ['jpg','bmp','png','gif'] 
path=os.path.join(root,"mydir_to_scan")
for r,d,f in os.walk(path):
    for file in f:
        if file[-3:] in alist_filter and pattern in file:
            print os.path.join(root,file)

无需切片;file.endswith(alist_filter)足够。
马丁·彼得斯

5
import os

dir="/path/to/dir"
[x[0]+"/"+f for x in os.walk(dir) for f in x[2] if f.endswith(".jpg")]

这将为您提供jpg文件及其完整路径的列表。您可以替换x[0]+"/"+ff的只是文件名。您也可以f.endswith(".jpg")用所需的任何字符串条件替换。


3

您可能还需要更高级的方法(我已经实现并打包为findtools):

from findtools.find_files import (find_files, Match)


# Recursively find all *.txt files in **/home/**
txt_files_pattern = Match(filetype='f', name='*.txt')
found_files = find_files(path='/home', match=txt_files_pattern)

for found_file in found_files:
    print found_file

可以安装

pip install findtools

2

“ path / to / images”中带有“ jpg”和“ png”扩展名的文件名:

import os
accepted_extensions = ["jpg", "png"]
filenames = [fn for fn in os.listdir("path/to/images") if fn.split(".")[-1] in accepted_extensions]

这是非常相似,由@ ramsey0给出的答案
CHB

1

您可以使用Python标准库3.4及更高版本中提供的pathlib

from pathlib import Path

files = [f for f in Path.cwd().iterdir() if f.match("145592*.jpg")]

1

您可以定义模式并进行检查。在这里,我采用了开始和结束模式,并在文件名中查找它们。FILES包含目录中所有文件的列表。

import os
PATTERN_START = "145592"
PATTERN_END = ".jpg"
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
for r,d,FILES in os.walk(CURRENT_DIR):
    for FILE in FILES:
        if PATTERN_START in FILE and PATTERN_END in FILE:
            print FILE


-1

您可以使用subprocess.check_ouput()作为

import subprocess

list_files = subprocess.check_output("ls 145992*.jpg", shell=True) 

当然,引号之间的字符串可以是您要在shell中执行并存储输出的任何内容。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.