使用jq,如何根据对象属性的值将JSON对象流拆分为单独的文件?

问题描述 投票:0回答:2

我有一个非常大的文件(20GB +压缩),名为input.json,包含一个JSON对象流,如下所示:

{
    "timestamp": "12345",
    "name": "Some name",
    "type": "typea"
}
{
    "timestamp": "12345",
    "name": "Some name",
    "type": "typea"
}
{
    "timestamp": "12345",
    "name": "Some name",
    "type": "typeb"
}

我想将此文件拆分为依赖于其type属性的文件:typea.jsontypeb.json等,每个文件都包含自己的json对象流,这些对象只具有匹配的type属性。

我已经设法为较小的文件解决了这个问题,但是对于这么大的文件,我的AWS实例上的内存耗尽了。由于我希望减少内存使用量,我知道我需要使用--stream,但我很难看到如何实现这一目标。

cat input.json | jq -c --stream 'select(.[0][0]=="type") | .[1]'将返回每个类型属性的值,但是如何使用它来过滤对象呢?

任何帮助将不胜感激!

json bash stream jq partitioning
2个回答
0
投票

假设文件中的JSON对象相对较小(不超过几MB),则不需要使用(相当复杂的)“ - stream”命令行选项,这在输入时主要需要(或者包括)一个单一的JSON实体。

然而,仍有几种选择。主要的是在Split a JSON file into separate files描述,这些是多遍方法(N或(N + 1)调用jq,其中N是输出文件的数量),并且只涉及一次调用jq的方法,其次是调用awk等程序来执行实际的文件分区。每种方法都有其优点和缺点,但如果读取输入文件N次是可以接受的,那么第一种方法可能会更好。

要估计所需的总计算资源,测量运行jq empty input.json所使用的资源可能是一个好主意。

(从你的简短写作来看,这听起来就像你遇到的内存问题主要来自解压缩文件。)


0
投票

使用jq拆分为NUL分隔的(类型,文档)对流,并使用本机bash(4.1或更高版本)使用一组持久的文件描述符写入这些文档:

#!/usr/bin/env bash
case $BASH_VERSION in ''|[1-3].*|4.0*) echo "ERROR: Bash 4.1 needed" >&2; exit 1;; esac

declare -A output_fds=( )

while IFS= read -r -d '' type && IFS= read -r -d '' content; do
  if [[ ${output_fds[$type]} ]]; then  # already have a file handle for this output file?
    curr_fd=${output_fds[$type]}       # reuse it, then.
  else
    exec {curr_fd}>"$type.json"        # open a new output file...
    output_fds[$type]=$curr_fd         # and store its file descriptor for use.
  fi
  printf '%s\n' "$content" >&"$curr_fd"
done < <(jq -j '(.type) + "\u0000" + (. | tojson) + "\u0000"')

这从不会一次读入多个记录(不可否认,每个记录可能是多个副本)到内存中,所以只要记录大小合适,它就可以使用任意大的文件。

© www.soinside.com 2019 - 2024. All rights reserved.