分离处理非常大的压缩 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 文件包含带有嵌入式文字换行符的字段;如果中场分裂,他们可能会变得畸形。
好的,让我们从我正在使用的命令行开始:
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 文件包含带有嵌入式文字换行符的字段;如果中场分裂,他们可能会变得畸形。