分离处理非常大的压缩 JSON 文件的命令的保存输出

separate the saved output of command that process a very large compressed JSON file

好的,让我们从我正在使用的命令行开始:

curl --silent http://example.com/json.gz | pigz -dc | jq -r '[.name, .value] | @csv' > data.csv

CURL 将下载压缩的 11.6 GB 的 JSON 文件,pigz 将解压缩并将所有处理后的输出写入标准输出,jq 将读取 JSON 和将输出保存为 csv 文件。

问题是,保存为 data.csv 的输出非常大,毕竟我仍然需要使用 PHP 脚本分析这些数据并将其插入到 MYSQL特殊格式(那时数据将非常小)

但是,我的服务器中只剩下不到 60 GB 的可用空间 space,即使我无法解压缩完整数据并将其保存到 CSV 文件中。

所以,我有一个想法,如果我能够将输出保存到具有不同名称的单独文件(假设名称是当前日期或时间戳),那么我可以 运行 PHP 脚本来处理它们的每个 .csv 文件并将数据保存到数据库,然后删除文件以释放 space,不确定这是否是最好的方法,但至少我正在尝试有用。

所以,我将命令行修改为:

curl --silent http://example.com/json.gz | pigz -dc | jq -r '[.name, .value] | @csv' > `date +"%S-%M-%d-%m-%Y"`_data.csv

但是,它只将所有内容保存在一个文件中,我认为它将保存为多个文件,每个文件都有不同的名称,因为在写入输出时日期会不断变化。

此外,欢迎任何其他可行的解决方案,谢谢!

我建议使用 awk 之类的程序进行分区,例如像这样:

jq -rc '[.id, .value] | @csv' |
  awk -v NUM 100000 '{n++; print > "out." int((n+NUM)/NUM) ".csv"}'

使用split命令,见man-page

简单示例(10MB 到 STDOUT):

# dd if=/dev/zero bs=1M count=10 | split - --bytes=1M -d -a3 out

输出文件(从STDIN读取大小为1MB的10个文件):

# stat -c "%s %n" out00*
1048576 out000
1048576 out001
1048576 out002
1048576 out003
1048576 out004
1048576 out005
1048576 out006
1048576 out007
1048576 out008
1048576 out009

或者用split --bytes=1M -d -a3 out out

拆分保存的文件

输出:

# stat -c "%s %n" out*
10485760 out
1048576 out000
1048576 out001
1048576 out002
1048576 out003
1048576 out004
1048576 out005
1048576 out006
1048576 out007
1048576 out008
1048576 out009

使用 GNU split --filter

保存 space

POSIX split 从其输入创建输出文件,因此需要大量空闲 space 来存储它们(整个未压缩输入的大小加上一些开销)。

但是,split 的 GNU 版本有一个额外的 --filter 选项,允许处理单个数据块的时间要少得多 space,因为它不需要创建任何临时文件:

| split -l $NUMLINES --filter='shell_command'

除了将数据传递给标准输入而不是作为命令行参数外,您可以将其视为 xargs -n $NUMLINES command

比如输出每组(最多)7行的md5sum/etc/passwd然后输出处理的chunk数:

</etc/passwd split -l7 --filter='md5sum|tee /dev/tty' |\
{ echo Processed $(wc -l) chunks; }

要修改您的命令一次处理 10000 行,您可以这样做:

curl -L --silent "$URL" |\
pigz -dc |\
jq -r '[.name, .value] | @csv' |\
split -l 10000 --filter='save2db.php'

您的过滤器命令 save2db.php 应该从标准输入读取。

如果您更喜欢从实际文件中读取它,您可以这样做:

... |\
split -l 10000 --filter='cat >TMPFILE; save2db.php TMPFILE';
rm TMPFILE

警告:您需要确保在行边界上拆分 csv 文件是安全的。一些 csv 文件包含带有嵌入式文字换行符的字段;如果中场分裂,他们可能会变得畸形。