为什么 os.scandir() 变慢/如何重新组织大目录?

问题描述

我有一个包含 300 万多个文件的目录(我应该首先避免创建)。使用 os.scandir() 简单地打印出名称

for f in os.scandir():
    print(f)

对于前大约 200,000 个文件,每个项目需要 0.004 秒,但每个项目大幅减慢到 0.3 秒。再次尝试时,它做了同样的事情 - 前 ~200,000 快速,然后放慢了速度。

等待一个小时并再次运行后,这一次前 ~400,000 个文件的速度很快,但随后以同样的方式变慢。

这些文件都是从 1908 年到 1963 年之间的一年开始的,所以我尝试使用像

这样的 bash 命令重新组织文件
for i in {1908..1963}; do 
> mkdir ../test-folders/$i; 
> mv $i* ../test-folders/$i/; 
> done

但它最终被挂断了,永远无法到达任何地方......

关于如何重新组织这个巨大的文件夹或更有效地列出目录中的文件的任何建议?

解决方法

这听起来像使用迭代器,一个一次只返回一个项目而不是把所有东西都放在内存中的函数,是最好的。

glob 库具有函数 iglob

for infile in glob.iglob( os.path.join(rootdir,'*.*') ):
    …

文档:https://docs.python.org/3/library/glob.html#glob.iglob

相关问答:https://stackoverflow.com/a/17020892/7838574

,

哦。那是很多文件。我不确定为什么 python 开始变慢,这很有趣。但是,您遇到问题的原因有很多。一,目录可以被认为是一种特殊类型的文件,它只保存其中所有文件的文件名/数据指针(非常简化)。当操作系统将某些信息缓存在内存中以加快整个系统的磁盘访问速度时,它可以更快地访问任何文件。

python变得更慢似乎很奇怪,也许您正在使用python中的内部存储器或其他机制。

但是让我们解决问题的根源。您的 bash 脚本有问题,因为每次使用 * 字符时,您都会强制 bash 脚本读取整个目录(并可能按字母顺序对其进行排序)。获取列表一次然后对列表的部分进行操作可能更明智。也许是这样的:

/bin/ls -1 > /tmp/allfiles
for i in {1908..1963}; do
    echo "moving files starting with $i"
    mkdir ../test-folders/$i
    mv $(egrep "^$i" /tmp/allfiles) ../test-folders/$i/
done

这只会读取目录一次(有点),并会通知您它的运行速度。