(已解决)如何在不耗尽内存的情况下使用jq读取100GB以上的文件



我有一个100+GB的json文件,当我尝试用jq读取它时,我的计算机一直在运行我们的of ram。有没有一种方法可以在限制内存使用的同时读取文件,或者有其他方法可以读取非常大的json文件?

我在命令中键入的内容:jq 'keys' fileName.json

确定包含单个JSON实体的非常大的文件的结构的一种通用方法是运行以下查询:

jq -nc --stream -f structural-paths.jq huge.json | sort -u

其中structural_paths.jq包含:

inputs
| select(length == 2)
| .[0]
| map( if type == "number" then 0 else . end )

请注意,输出中的"0"表示在相应位置至少有一个有效的数组索引,而不是"0"实际上是该位置的有效索引。

还要注意,对于非常大的文件,使用jq-stream处理整个文件可能会非常慢。

示例:

给定{"a": {"b": [0,1, {"c":2}]}},上述咒语的结果将是:

["a","b",0,"c"]
["a","b",0]

顶层结构

如果你只是想了解更多关于顶层结构的信息,你可以将上面的jq程序简化为:

inputs | select(length==1)[0][0] | if type == "number" then 0 else . end

给定深度的结构

如果命令行sort失败,那么您可能希望通过仅将路径考虑到一定深度来限制路径的数量。

如果深度不是太大,那么希望您的命令行sort能够管理;如果没有,那么使用命令行uniq至少会对输出进行一些微调。

一个更好的选择可能是在jq中定义unique(stream),然后使用它,如图所示:

# Output: a stream of the distinct `tostring` values of the items in the stream
def uniques(stream):
foreach (stream|tostring) as $s ({};
if .[$s] then .emit = false else .emit = true | .item = $s | .[$s]=true end;
if .emit then .item else empty end );
def spaths($depth):
inputs
| select(length==1)[0][0:$depth]
| map(if type == "number" then 0 else . end);
uniques(spaths($depth))

一个合适的jq调用看起来像:

jq -nr --argjson depth 3 --stream -f structural-paths.jq huge.json

除了避免排序成本外,使用uniques/1还将保留原始JSON中路径的顺序。

"JSON指针";指针

如果要将数组路径表达式转换为";JSON指针";字符串(例如,用于jmjstream(,只需将以下内容附加到相关的jq程序:

| "/" + join("/")

jq的流式解析器(使用--stream选项调用(通常可以处理非常非常大的文件(如果满足某些条件,甚至可以处理任意大的文件(,但通常速度非常慢,而且非常麻烦。

在实践中,我发现像jstream和/或我自己的jm这样的工具在处理大量文件时与jq配合使用非常好。当以这种方式使用时,它们都非常容易使用,尽管安装可能会有点麻烦。

不幸的是,如果您对JSON文件的内容一无所知,除了jq empty花费太长时间或失败之外,那么我所知道的任何CLI工具都无法自动生成有用的模式。然而,查看文件的前几个字节通常会提供足够的信息。或者,您可以从jm count开始计算顶级对象,然后从那里开始。如果顶级是JSON对象,jm -s | jq 'keys[]'将为您提供顶级键的列表。


这里有一个例子。假设我们已经确定文件ginormous.json的大尺寸主要是因为它由一个非常长的顶级数组组成。然后假设schema.jq(本页其他地方已经提到(在pwd中,您有希望通过运行找到一个信息丰富的模式

jm ginormous.json |
jq -n 'include "schema" {source:"."}; schema(inputs)'

另请参阅jq以递归方式评测JSON对象,以获得更简单的模式推理引擎。

我在这里发布了一个相关的问题:slurp、null输入和输入过滤器之间的区别

如果你的文件很大,但文件中的文档没有那么大(只有很多小的(,jq -n 'inputs'可以让你开始:

jq -n 'inputs | keys'

这里有一个例子(用一个小文件(:

$ jq -n 'inputs | keys' <<JSON
{"foo": 21, "bar": "less interesting data"}
{"foo": 42, "bar": "more interesting data"}
JSON
[
"bar",
"foo"
]
[
"bar",
"foo"
]

如果您有一个千兆字节大或有数百万个密钥的顶级对象,则这种方法将不起作用。

最新更新