我有一个大小为1.5 GB的zip文件。
它的内容是一个荒谬的大型纯文本文件(60 GB),并且我目前在磁盘上没有足够的空间来提取所有文件,即使有,我也不想提取所有文件。
至于我的用例,只要我可以检查部分内容就足够了。
因此,我想将文件解压缩为流并访问文件的范围(就像可以通过普通文本文件的头部和尾部通过)。
通过内存(例如,从32GB标记开始提取最大100kb)或通过行(给我纯文本行3700-3900)。
有办法实现吗?
我有一个大小为1.5 GB的zip文件。
它的内容是一个荒谬的大型纯文本文件(60 GB),并且我目前在磁盘上没有足够的空间来提取所有文件,即使有,我也不想提取所有文件。
至于我的用例,只要我可以检查部分内容就足够了。
因此,我想将文件解压缩为流并访问文件的范围(就像可以通过普通文本文件的头部和尾部通过)。
通过内存(例如,从32GB标记开始提取最大100kb)或通过行(给我纯文本行3700-3900)。
有办法实现吗?
Answers:
请注意,它gzip
可以提取zip
文件(至少是文件中的第一个条目zip
)。因此,如果该存档中只有一个大文件,则可以执行以下操作:
gunzip < file.zip | tail -n +3000 | head -n 20
例如,提取从第3000行开始的20行。
要么:
gunzip < file.zip | tail -c +3000 | head -c 20
对于具有字节的同一事物(假设head
实现支持-c
)。
对于档案中的任意成员,采用Unixy方式:
bsdtar xOf file.zip file-to-extract | tail... | head...
借助的head
内置功能ksh93
(例如何时/opt/ast/bin
在前面$PATH
),您还可以执行以下操作:
.... | head -s 2999 -c 20
.... | head --skip=2999 --bytes=20
请注意,在任何情况下,gzip
/ bsdtar
/ unzip
始终需要解压缩(并在此处丢弃)导致指向您要提取的部分的文件的整个部分。这取决于压缩算法的工作方式。
gzip
可以处理它,将其他“Z意识到”实用工具(zcat
,zless
,等),也包括工作?
gzip
(通常为true zless
,zcat
在某些系统上不一定是.Z
仅读取文件),是的。
一种使用unzip -p和dd的解决方案,例如提取具有1000个整体偏移的10kb:
$ unzip -p my.zip | dd ibs=1024 count=10 skip=1000 > /tmp/out
注意:我并没有尝试使用非常庞大的数据...
unzip -l ARCHIVE
该列表列出存档内容,并将unzip -p ARCHIVE PATH
单个对象的内容提取PATH
到stdout。
dd
与计数管或跳过是不可靠的,因为它会做很多read()
第可达 1024个字节。因此,只有按unzip
大小为1024的倍数的块写入管道时,才能保证正常工作。
如果你有过创造,大的zip文件的控制,为什么不考虑使用的组合gzip
和zless
?
这样一来,您就可以zless
用作传呼机并查看文件的内容,而不必担心提取过程。
如果您无法更改压缩格式,则显然无法正常工作。如果是这样,我觉得zless
很方便。
要查看文件的特定行,请将输出通过管道传递到Unix流编辑器sed。这可以处理任意大的数据流,因此您甚至可以使用它来更改数据。要按照要求查看3700-3900行,请运行以下命令。
unzip -p file.zip | sed -n 3700,3900p
sed -n 3700,3900p
将继续读取直到文件末尾。这是更好地使用sed '3700,$!d;3900q'
,以避免这种情况,甚至通常更有效:tail -n +3700 | head -n 201
我想知道是否有可能做比从文件开始到进行解压缩更有效的事情。看来答案是否定的。但是,在某些CPU(Skylake)zcat | tail
上,CPU不能提升至全时钟速度。见下文。定制解码器可以避免该问题并节省管道写入系统调用,并且可能快10%。(如果不调整电源管理设置,则在Skylake上快60%左右)。
使用具有skipbytes
功能的自定义zlib最好的办法是解析压缩块中的符号直到结束,而无需进行实际重建解压缩块的工作。这可能比调用zlib的常规解码函数覆盖相同的缓冲区并在文件中继续前进要快得多(大约至少2倍)。但是我不知道有人写过这样的函数。(而且我认为,除非专门编写文件以允许解码器在某个特定的块处重新启动,否则这实际上不会起作用)。
我希望有一种方法,通过放气块跳过无需解码他们,因为那将是多快。霍夫曼树在每个块的开头发送,因此您可以从任何块的开头解码(我认为)。哦,我认为解码器的状态不仅仅是霍夫曼树,它还是以前的32kiB解码数据,并且默认情况下不会跨块边界重置/遗忘该状态。相同的字节可以继续被重复引用,因此,在一个巨大的压缩文件中,它实际上只能出现一次。(例如,在日志文件中,主机名可能始终在压缩字典中保持“热门”状态,并且主机名的每个实例都引用前一个而不是第一个)。
该zlib
手册说,如果要使压缩流在这一点上可搜索,则必须Z_FULL_FLUSH
在调用时使用deflate
。它“重置压缩状态”,因此,我认为如果没有这种压缩,则向后引用可以进入先前的块。因此,除非您的zip文件偶尔写有全刷新块(例如每1G或对压缩的影响微不足道),否则我认为您需要做的解码工作要比我最初多得多思维。我想您可能无法从任何块的开头开始。
余下的内容是在我认为可以找到包含所需第一个字节的块的开头并从此处进行解码时编写的。
但是不幸的是,对于压缩块,Deflate块的开始并没有指示它多长时间。不可压缩的数据可以使用未压缩的块类型进行编码,该块类型的前端具有16位大小(以字节为单位),但是压缩块则不能:RFC 1951很好地描述了这种格式。具有动态霍夫曼编码的块的树位于块的前面(因此解压缩器不必在流中查找),因此压缩器必须在写入之前将整个(压缩的)块保留在内存中。
最大后向参考距离仅为32kiB,因此压缩器不需要在内存中保留大量未压缩的数据,但这并没有限制块的大小。块的长度可以是几兆字节。(如果可以在不解析的情况下找到当前块的末尾,那么即使是在磁驱动器上,这也足够使磁盘搜索值得,而不是顺序读取到内存中,而只是跳过RAM中的数据)。
zlib使块尽可能长: 根据Marc Adler的说法,zlib仅在符号缓冲区填满时才开始新的块,其默认设置为16,383个符号(文字或匹配项)
我压缩了输出seq
(这是非常冗余的,因此可能不是很好的测试),但是pv < /tmp/seq1G.gz | gzip -d | tail -c $((1024*1024*1000)) | wc -c
在带DDR4-2666 RAM的3.9GHz的Skylake i7-6700k上,压缩数据的运行速度仅为〜62 MiB / s。解压缩后的数据为246MiB / s,与memcpy
块大小太大而无法放入缓存的〜12 GiB / s的速度相比,这是块变化。
(将其energy_performance_preference
设置为默认值,balance_power
而不是balance_performance
,Skylake的内部CPU调节器决定仅以2.7GHz运行,约43 MiB / s的压缩数据。我曾经对其sudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'
进行过调整。这种频繁的系统调用可能看起来并不像真正的CPU限制向电源管理单元工作。)
TL:DR:zcat | tail -c
除非您的磁盘速度非常慢,否则即使在快速CPU上也受CPU限制。 gzip使用了100%的CPU(根据,每个时钟运行1.81条指令perf
),并tail
使用了0.162的CPU(0.58 IPC)。否则,该系统通常处于空闲状态。
我使用的是Linux 4.14.11-1-ARCH,默认情况下启用了KPTI来解决Meltdown问题,因此所有这些write
系统调用gzip
都比以前昂贵:/
将搜索内置于unzip
或zcat
(但仍使用常规zlib
解码功能)将节省所有这些管道写入操作,并使Skylake CPU以全时钟速度运行。(针对某些负载的这种降频功能是Intel Skylake及更高版本所独有的,它们可以从OS上减轻CPU频率决策的负担,因为它们拥有有关CPU所执行操作的更多数据,并且可以更快地进行上升/下降。这是通常情况下很好,但这会导致Skylake在调速器设置较为保守的情况下无法全速前进)。
没有系统调用,只是重写适合L2高速缓存的缓冲区,直到到达所需的起始字节位置为止,这至少可能会产生%的差异。甚至10%,但我只是在这里补编数字。我没有zlib
详细介绍过它的缓存占用空间,以及在启用KPTI的情况下每个系统调用上的TLB刷新(以及uop-cache刷新)有多大。
有一些软件项目确实向gzip文件格式添加了搜索索引。如果您无法让任何人为您生成可搜索的压缩文件,那么这对您没有帮助,但是其他未来的读者可能会受益。
想必没有这些项目的有,知道如何通过放气流跳过没有索引,因为他们只是设计工作时,索引的解码功能是可用的。