我有一个.tar.gz文件。它包含一个 20GB 大小的文本文件,有 2050 万行。我无法将此文件作为一个整体提取并保存到磁盘。我必须执行以下任一选项:
- 在每个文件中指定行数(例如 100 万行),并获取 21 个文件。这将是首选选项。
- 根据行号提取该文件的一部分,即从1000001到2000001,以获得具有 1M 行的文件。我将不得不使用不同的参数重复此步骤 21 次,这非常糟糕。
可能吗?
这个答案 - bash:仅提取部分 tar.gz 存档 - 描述了一个不同的问题。
要从f.tar.gz
中提取文件并将其拆分为文件,每个文件不超过 100 万行,请使用:
tar Oxzf f.tar.gz | split -l1000000
以上将按默认方法命名输出文件。 如果您希望将输出文件命名为 prefix.nn,其中 nn 是序列号,请使用:
tar Oxzf f.tar.gz |split -dl1000000 - prefix.
在这种方法下:
原始文件永远不会写入磁盘。
tar
从.tar.gz
文件中读取并将其内容通过管道传输到split
,后者在将片段写入磁盘之前将其分成多个部分。.tar.gz
文件仅读取一次。split
,通过其众多选择,具有很大的灵活性。
解释
对于tar
命令:
O
告诉tar
将输出发送到标准输出。 这样,我们可以将其通过管道传输到split
,而无需将原始文件保存在磁盘上。x
告诉tar
提取文件(而不是创建存档)。z
告诉tar
存档是gzip格式。 在现代焦油上,这是可选的f
告诉tar
使用指定的文件名作为输入。
对于split
命令:
-l
告诉split
拆分受行数(而不是字节)限制的文件。-d
告诉split
对输出文件使用数字后缀。-
告诉split
从stdin获取输入
您可以使用 tar 中的 --to-stdout(或 -O)选项将输出发送到 stdout。然后使用 sed 指定所需的行集。
#!/bin/bash
l=1
inc=1000000
p=1
while test $l -lt 21000000; do
e=$(($l+$inc))
tar -xfz --to-stdout myfile.tar.gz file-to-extract.txt |
sed -n -e "$l,$e p" > part$p.txt
l=$(($l+$inc))
p=$(($p+1))
done
这是选项 #1 的纯 Bash 解决方案,自动将行拆分为多个输出文件。
#!/usr/bin/env bash
set -eu
filenum=1
chunksize=1000000
ii=0
while read line
do
if [ $ii -ge $chunksize ]
then
ii=0
filenum=$(($filenum + 1))
> out/file.$filenum
fi
echo $line >> out/file.$filenum
ii=$(($ii + 1))
done
这将从 stdin 中获取任何行并创建文件,例如具有前百万行的 out/file.1
,out/file.2
具有第二百万行等。 然后,您所需要的只是将输入提供给上面的脚本,如下所示:
tar xfzO big.tar.gz | ./split.sh
这永远不会将任何中间文件保存在磁盘上,甚至保存在内存中。 它完全是一个流媒体解决方案。 这有点浪费时间,但在空间方面非常有效。 它也非常便携,应该在 Bash 以外的外壳中工作,并且在几乎没有变化的古老系统上工作。
sed -n 1,20p /Your/file/Path
在这里,您提到您的第一行号和最后一行号我的意思是说这可能看起来像
sed -n 1,20p /Your/file/Path >> file1
并在变量中使用起始行号和结束行号并相应地使用它。