![高效合并 500 万个文件](https://linux22.com/image/180388/%E9%AB%98%E6%95%88%E5%90%88%E5%B9%B6%20500%20%E4%B8%87%E4%B8%AA%E6%96%87%E4%BB%B6.png)
由于一些糟糕的规划,我的目录包含超过 500 万个文件,总计大约 20GB。每个文件的顶部包含 32 行垃圾,后面跟着未知行数的重要数据。
我想将所有重要数据合并到一个文件中。
我正在这样做:
for i in $(find); do tail -n +32 $i >> ../all.txt; done
all.txt 每秒仅增长约 0.5MB。有没有更快的方法来完成这个任务?另外,删除文件会很方便,因为在完成工作之前我可能会用完磁盘空间:X
任何建议表示赞赏。
答案1
如果需要随时删除文件,那么您编写的内容已经是一种快速的方法。一种优化是,find
您可以使用 来列出文件内容,而不是使用*
来列出文件,因为它们在目录列表中列出,而不会产生额外的处理时间find
。也就是说,你会写:
for i in *; do tail -n +3 $i >> ../x; rm $i; done
但是,如果您可以在完成合并之前删除它们,并且如果您还希望保留哪些内容来自哪个文件,则有一种方法可以一次解析它们以跟踪多个(与您的 shell 一样多)允许)。执行此操作的命令是:
find . -exec tail -n +3 {} >> ../x +
最后+
会告诉find
一次传递多个文件名,而不是一次传递一个。这将导致性能的巨大提高(由于调用的实例数量少得多tail
),但您的输出文件仍然会有类似的内容
==> ./filename <==
每次一个文件结束而下一个文件开始时打印。此外,这些文件不会随您删除。
如果您想以牺牲一点速度为代价删除上面的行,您可以通过以下方式运行它
find . -exec awk 'FNR>32' {} +
(感谢 dave_thompson 的建议作为评论)。
最后,如果您更喜欢列出哪些信息来自哪个文件的输出,并且仍然想随时删除它们,您可以使用 iruvar 的答案来执行此操作,将两个\;
' ' 替换为+
' ' ' (我最初的答案仅描述了要做什么; iruvar 给出了实际的命令)。
答案2
您的流程可能受 IO 限制,因此优化循环最多只能产生边际改进。如果您可以对文件进行深度优先操作,则可以将tail
和rm
调用滚动到 中find
,并在进行时删除
使用 GNU find
:
find . -type f -exec tail -n +32 {} \; -delete >../all.txt
理想情况下,我们希望能够通过替换来一次find
传递多个文件,但我似乎无法让它与tail
\;
+
-delete
缺乏find
选择-delete
的
find . -type f -exec tail -n +32 {} \; -exec rm {} \; >../all.txt
或者在 GNU 系统上,允许传递-q
to 来在接收多个文件tail
时抑制文件头的打印:tail
find . -type f -exec tail -q -n +32 {} + -exec rm {} + >../all.txt
注意 - 对于-exec rm
,适用于的深度优先搜索-delete
不再适用 这最后一个咒语可能是其中最高效的