目标
使用GNU Parallel将大型.gz文件拆分为子文件。由于服务器有16个CPU,因此创建16个子节点。每个孩子最多应包含N行。这里,N = 104,214,420行。儿童应该是.gz格式。
输入文件
硬件
代码
版本1
zcat "${input_file}" | parallel --pipe -N 104214420 --joblog split_log.txt --resume-failed "gzip > ${input_file}_child_{#}.gz"
三天后,工作没有完成。 split_log.txt为空。输出目录中没有可见的子项。日志文件表明Parallel已将--block-size
从1 MB(默认值)增加到2 GB以上。这激发了我将代码更改为版本2.
第2版
# --block-size 3000000000 means a single record could be 3 GB long. Parallel will increase this value if needed.
zcat "${input_file}" | "${parallel}" --pipe -N 104214420 --block-size 3000000000 --joblog split_log.txt --resume-failed "gzip > ${input_file}_child_{#}.gz"
这份工作已经运行了大约2个小时。 split_log.txt为空。尚未在输出目录中看到子项。到目前为止,日志文件显示以下警告:
parallel: Warning: --blocksize >= 2G causes problems. Using 2G-1.
问题
答案 0 :(得分:2)
我们假设该文件是一个fastq文件,因此记录大小为4行。
你告诉GNU Parallel -L 4
。
在fastq文件中,顺序无关紧要,因此您希望将n * 4行的块传递给子项。
要有效地执行此操作,请使用--pipe-part
,但--pipe-part
不适用于压缩文件且不适用于-L
,因此您必须选择--pipe
。
zcat file1.fastq.gz | parallel -j16 --pipe -L 4 --joblog split_log.txt --resume-failed "gzip > ${input_file}_child_{#}.gz"
这会将一个块传递给16个子节点,并且一个块默认为1 MB,它在记录边界(即4行)处被切断。它将为每个块运行一个作业。但你真正想要的是将输入传递给总共只有16个工作,你可以这样做循环。遗憾的是,--round-robin
中存在随机元素,因此--resume-failed
无效:
zcat file1.fastq.gz | parallel -j16 --pipe -L 4 --joblog split_log.txt --round-robin "gzip > ${input_file}_child_{#}.gz"
parallel
将努力跟上16 gzips,但你应该能够压缩100-200 MB / s。
现在如果你有fastq文件未压缩我们可以更快地完成它,但我们将不得不作弊:通常在fastq文件中你将有一个seqname,它启动相同的字符串:
@EAS54_6_R1_2_1_413_324
CCCTTCTTGTCTTCAGCGTTTCTCC
+
;;3;;;;;;;;;;;;7;;;;;;;88
@EAS54_6_R1_2_1_540_792
TTGGCAGGCCAAGGCCGATGGATCA
+
;;;;;;;;;;;7;;;;;-;;;3;83
@EAS54_6_R1_2_1_443_348
GTTGCTTCTGGCGTGGGTGGGGGGG
+EAS54_6_R1_2_1_443_348
;;;;;;;;;;;9;7;;.7;393333
这是@EAS54_6_R
。不幸的是,这也是质量线中的有效字符串(这是真正的哑设计),但实际上,我们会非常惊讶地看到以@EAS54_6_R
开头的质量线。它不会发生。
我们可以使用它,因为现在您可以使用\n
后跟@EAS54_6_R
作为记录分隔符,然后我们可以使用--pipe-part
。额外的好处是订单将保持不变。在这里,您必须将块大小设置为file1-fastq
的大小的1/16:
parallel -a file1.fastq --block <<1/16th of the size of file1.fastq>> -j16 --pipe-part --recend '\n' --recstart '@EAS54_6_R' --joblog split_log.txt "gzip > ${input_file}_child_{#}.gz"
如果你使用GNU Parallel 20161222,那么GNU Parallel可以为你做这个计算。 --block -1
表示:选择一个块大小,以便您可以为16个作业点中的每一个提供一个块。
parallel -a file1.fastq --block -1 -j16 --pipe-part --recend '\n' --recstart '@EAS54_6_R' --joblog split_log.txt "gzip > ${input_file}_child_{#}.gz"
这里GNU Parallel不是限制因素:它可以轻松传输20 GB / s。
打开文件以查看重新启动值应该是什么很烦人,所以这在大多数情况下都有效:
parallel -a file1.fastq --pipe-part --block -1 -j16
--regexp --recend '\n' --recstart '@.*\n[A-Za-z\n\.~]'
my_command
这里我们假设这些行将像这样开始:
@
[A-Za-z\n\.~]
anything
anything
即使您有一些以'@'开头的质量线,那么它们也永远不会出现以[A-Za-z \ n。〜]开头的行,因为质量线后面总是跟着seqname行,以@。
开头你也可能有一个块大小,它相当于未压缩文件的1/16,但这是一个坏主意:
gzip
只会在读完最后一个字节后启动(并且当时可能会完成第一个gzip
)。通过将记录数设置为104214420(使用-N),这基本上就是你正在做的事情,而你的服务器可能很难将150 GB的未压缩数据保存在36 GB的RAM中。
答案 1 :(得分:1)
配对结束有限制:订单无关紧要,但订单必须可以预测不同的文件。例如。 file1.r1.fastq.gz中的record n必须与file1.r2.fastq.gz中的记录n匹配。
split -n r/16
对于执行简单的循环非常有效。但是,它不支持多行记录。所以我们在每第4行之后插入\ 0作为记录分隔符,我们在拆分后删除它。 --filter
在输入上运行命令,因此我们不需要保存未压缩的数据:
doit() { perl -pe 's/\0//' | gzip > $FILE.gz; }
export -f doit
zcat big.gz | perl -pe '($.-1)%4 or print "\0"' | split -t '\0' -n r/16 --filter doit - big.
文件名将命名为big.aa.gz
.. big.ap.gz
。