如何在没有中间文件的情况下将多个文件合并为一个? [关闭]
Posted
技术标签:
【中文标题】如何在没有中间文件的情况下将多个文件合并为一个? [关闭]【英文标题】:How can I cat multiple files together into one without intermediary file? [closed] 【发布时间】:2011-05-03 14:37:29 【问题描述】:这是我面临的问题:
我正在处理一个大小约为 100G 的文本文件的字符串。 我正在尝试通过将文件拆分为数百个文件来改进运行时间 较小的文件并并行处理它们。 最后,我将生成的文件按顺序重新组合在一起。文件读/写时间本身需要几个小时,所以我想找到一种方法来改进以下内容:
cat file1 file2 file3 ... fileN >> newBigFile
这需要双倍的磁盘空间,因为file1
... fileN
占用 100G,然后newBigFile
占用另一个 100Gb,然后 file1
...fileN
被删除。
数据已经在file1
... fileN
,执行cat >>
会导致读取
并写下我真正需要的只是数百个文件的时间
重新出现为 1 个文件...
【问题讨论】:
听起来你应该使用比 Unix shell 更强大的东西。 我不知道我在说什么,但是否可以操纵文件记录或其他东西?因为我需要做的不是复制数据,而是将多个文件串在一起重新组合成 1? 已编辑的问题放入重新开放队列(现在不太自以为是)因为这个问题的答案是太棒了! 0:-) 【参考方案1】:如果您不需要随机访问最终的大文件(即,您只需从头到尾通读一遍),您可以将数百个中间文件显示为一个。你通常会做的地方
$ consume big-file.txt
改为
$ consume <(cat file1 file2 ... fileN)
这使用 Unix process substitution,有时也称为“匿名命名管道”。
您还可以通过拆分输入并同时进行处理来节省时间和空间; GNU Parallel 有一个 --pipe switch 可以做到这一点。它还可以将输出重新组合成一个大文件,可能会使用更少的暂存空间,因为它只需要一次将 number-of-cores 个片段保存在磁盘上。如果您实际上同时运行数百个进程,Parallel 将通过让您调整机器的并行量来大大提高您的效率。我强烈推荐它。
【讨论】:
我没有测试过这个,但听起来是最有用的建议 进程替换看起来很棒,因为它不会把东西放在磁盘上。所以你可以做“消费 【参考方案2】:将文件重新连接在一起时,您可以删除附加的小文件:
for file in file1 file2 file3 ... fileN; do
cat "$file" >> bigFile && rm "$file"
done
这将避免需要双倍的空间。
没有其他方法可以神奇地使文件神奇地连接起来。文件系统 API 根本没有执行此操作的函数。
【讨论】:
【参考方案3】:也许dd
会更快,因为您不必在 cat 和 shell 之间传递东西。比如:
mv file1 newBigFile
dd if=file2 of=newBigFile seek=$(stat -c %s newBigFile)
【讨论】:
我绝对认为 dd 与 Robie Basak 建议的在复制文件时删除文件相结合,将成为最重组的解决方案,而不是使用 mmap 实现自定义 cp/unlink 命令。然而,我相信没有什么比完全消除分裂更有效的了。【参考方案4】:我真正需要的只是将数百个文件重新显示为 1 个文件...
在文件系统级别以这种方式连接文件是不切实际的,因为文本文件通常不会完全填充磁盘块,因此必须向上移动后续文件中的数据以填补空白,导致一堆读/写无论如何。
【讨论】:
【参考方案5】:您是否可以简单地不拆分文件?而是通过在每个并行工作人员中设置文件指针来分块处理文件。如果需要以面向行的方式处理文件,这会使其变得更加棘手,但仍然可以完成。每个工作人员都需要了解,不是从您给它的偏移量开始,而是必须首先逐字节查找下一个换行符 +1。每个工作人员还必须了解,它不会处理您给它的设定数量的字节,而是必须在分配给它处理的设定数量的字节之后处理第一个换行符。
文件指针的实际分配和设置非常简单。如果有 n 个 worker,每个 worker 处理 n/file size 个字节,文件指针从 worker number * n/file_size 开始。
这种计划不够充分有什么原因吗?
【讨论】:
不是修改工人,shell 可以为工人提供一个stdin
,这已经是它应该处理的段,例如使用sed
来选择一个行范围。如果需要协调输出,GNU Parallel 可以提供帮助。
这整个事情是在 perl 中完成的,原始脚本尝试对整个 100G 文件串行进行字符串操作。现在我让它拆分文件并通过 fork() 处理块,但现在读/写时间成为运行时的瓶颈。我不必像你说的那样进行初始拆分,但我仍然必须写出处理过的块,然后将它们重新放在一个文件中,对吧?
如果我不拆分文件并让每个子进程读取在不同行工作的原始 100G 文件,我会因为 200 个进程尝试读取同一文件而遇到瓶颈吗?
@wing,您可能会遇到 io 争用,但很难预测,因为这取决于文件的碎片化方式、您拥有的存储类型等。更复杂的方法是让每个工作人员从主 i/o 读取进程分派的内存队列中读取工作,以便 io 读取是顺序的,但是这些执行中的任何一个都只能通过实验确定,因为我们无法轻松预测 OS 文件缓存等的影响.【参考方案6】:
我相信这是对同一文件夹中包含的所有文件进行分类的最快方法:
$ ls [path to folder] | while read p; do cat $p; done
【讨论】:
不错 =) 效果很好。需要回声;在完成之前。【参考方案7】:快速但不是免费的解决方案?获取基于 SSD 驱动器或闪存 PCIe 的存储。如果这是必须定期完成的事情,那么提高磁盘 IO 速度将是您可以获得的最具成本效益和最快的加速。
【讨论】:
谢谢,可惜我无法更改公司的文件服务器和硬件... 当然,您的情况可能会禁止这样做,但如果将其作为现有服务器磁盘存储的补充(而不是替换)提交给管理层,则可以考虑。如果您可以拥有仅用于此任务的 SSD,并且每天可以节省 2 小时的处理时间,我想他们会相信节省的成本。【参考方案8】:有太多并发这样的事情。
这样做的更好方法是在所需范围内对文件使用随机访问读取,并且从不实际拆分文件,并且仅处理文件数作为机器中物理 CPU/内核的数量。除非这也使 IOPS 淹没了磁盘,否则您应该减少直到磁盘不是瓶颈。
对于所有幼稚的拆分/复制/删除,您所做的任何一种方式都会产生大量的 IOPS,并且无法绕过它的物理特性。
除非这是一个持续的日常问题/问题,否则一个透明的解决方案可能比值得做的工作更多,是编写一个将单个文件表示为多个文件的自定义 FUSE 文件系统。有很多关于将存档文件内容作为单个文件处理的示例,这些示例将向您展示如何执行此操作的基础知识。
【讨论】:
是的,旧答案。除非存储子系统利用流式传输到随机访问存储的并发和并行子系统线程,否则多线程 io 拼接会更快,这是值得怀疑的。在没有这种能力的情况下,简单的 io 拼接或大块顺序复制将超过过度设计的实现的开销和复杂性,这种实现并没有更快,只是更难维护、调试和更慢。以上是关于如何在没有中间文件的情况下将多个文件合并为一个? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
如何在没有模块加载系统的情况下将我的 Typescript 编译成单个 JS 文件?
在 SQL 中,如何在不使用大小写的情况下将两个查询合并为一个?