我有大量文件,我想通过这些文件来执行md5校验和。
其中许多文件存储在多个物理磁盘上,但都安装在同一目录中:
/mnt/drive1/dir1/file.jpg
/mnt/drive2/dir1/file2.jpg
如何在不将整个目录和文件结构加载到内存的情况下递归/ mnt?
有多种方法可以使用多个线程吗?可能没有必要使用多个线程/进程递归目录,但文件操作可能是CPU密集型的,这将从多个CPU核心中受益。
提前致谢。
答案 0 :(得分:2)
为什么不使用简单的os.walk?它没有占用任何大量的记忆。
import os
for root, dirs, files in os.walk('/mnt'):
for name in files:
print os.path.join(root, name)
答案 1 :(得分:2)
稍微扩展我的注释,此代码创建一个进程池(在命令行上给出的大小),它打开当前目录下的每个文件,将其拉链50次,并计算其CRC。然后它将所有CRC共同扫描并打印出来。
import multiprocessing
import os
import sys
import zlib
NUM_PROCS = int(sys.argv[1])
def processFile(filepath):
infile = open(filepath, 'r')
contents = infile.read()
for i in xrange(50):
contents = zlib.compress(contents)
return zlib.crc32(contents)
def generateFilepaths():
for (dirpath, dirnames, filenames) in os.walk('.'):
for filename in filenames:
filepath = os.path.join(dirpath, filename)
yield filepath
if __name__ == '__main__':
pool = multiprocessing.Pool(NUM_PROCS)
fullCrc = 0
for crc in pool.imap_unordered(processFile, generateFilepaths()):
fullCrc ^= crc
print fullCrc
请注意,如果不对每个文件执行如此荒谬的工作量,此程序仍将完全受IO限制。因此,线程很可能会以极低的速度获得。
答案 2 :(得分:1)
import multiprocessing
import os.path
import hashlib
import sys
VALID_EXTENSIONS = ('.JPG', '.GIF', '.JPEG')
MAX_FILE_SZ = 1000000
def md5_file(fname):
try:
with open(fname) as fo:
m = hashlib.md5()
chunk_sz = m.block_size * 128
data = fo.read(chunk_sz)
while data:
m.update(data)
data = fo.read(chunk_sz)
md5_file.queue.put((fname, m.hexdigest()))
except IOError:
md5_file.queue.put((fname, None))
def is_valid_file(fname):
ext = os.path.splitext(fname)[1].upper()
fsz = os.path.getsize(fname)
return ext in VALID_EXTENSIONS and fsz <= MAX_FILE_SZ
def init(queue):
md5_file.queue = queue
def main():
# Holds tuple (fname, md5sum) / md5sum will be none if an IOError occurs
queue = multiprocessing.Queue()
pool = multiprocessing.Pool(None, init, [queue])
for dirpath, dirnames, filenames in os.walk(sys.argv[1]):
# Convert filenames to full paths...
full_path_fnames = map(lambda fn: os.path.join(dirpath, fn),
filenames)
full_path_fnames = filter(is_valid_file, full_path_fnames)
pool.map(md5_file, full_path_fnames)
# Dump the queue
while not queue.empty():
print queue.get()
return 0
if __name__ == '__main__':
sys.exit(main())
可能不是防弹,但它对我有用。你可能想要调整它以提供一些关于它正在做什么的反馈。
由于某些奇怪的原因,您无法共享全局队列。所以,我不得不使用池的initializer
函数。我不确定为什么会这样。
只需将根目录作为唯一参数传递给它,它将在完成后转储md5总和。
答案 3 :(得分:0)
不确定是否有一个普遍正确的答案,但也许你想从一些简单的,基准的开始,看看有什么瓶颈。
选项1
使用find /mnt -type f
生成文件列表并将其传递给脚本。这可以使用多个工作线程(multiprocessing
)完美地并行化,但您可能希望根据物理设备拆分列表,以便每个线程处理每个线程。如果连接的存储空间很慢,这可能很重要。
选项2
生成顶级目录列表(例如,max-depth 1或2),让线程分别在每个分区上运行。这可以通过Linux'find -typde d
和/或Python os.walk()
完成。
只要您不确切知道它有多糟糕,我就不会费心加载到内存中。也就是说,在你做基准之前。另外,我认为os.walk
或文件列表不会成为严重的内存问题。如果我错了,请纠正我。