使用 jq,如何根据对象 属性 的值将 JSON 对象流拆分为单独的文件?
Using jq, how can I split a JSON stream of objects into separate files based on the values of an object property?
我有一个名为 input.json
的非常大的文件(压缩后超过 20GB),其中包含一个 JSON 对象流,如下所示:
{
"timestamp": "12345",
"name": "Some name",
"type": "typea"
}
{
"timestamp": "12345",
"name": "Some name",
"type": "typea"
}
{
"timestamp": "12345",
"name": "Some name",
"type": "typeb"
}
我想根据 type
属性 将此文件拆分为多个文件:typea.json
、typeb.json
等,每个文件都包含自己的 json 仅具有匹配类型 属性.
的对象
我已经设法为较小的文件解决了这个问题,但是对于如此大的文件,我的 AWS 实例 运行 内存不足。因为我希望降低内存使用率,所以我知道我需要使用 --stream
,但我正在努力寻找如何实现这一点。
cat input.json | jq -c --stream 'select(.[0][0]=="type") | .[1]'
会 return 我得到每个类型属性的值,但是我如何使用它来过滤对象?
如有任何帮助,我们将不胜感激!
假设文件中的 JSON 对象相对较小(none 超过几 MB),您将不需要使用(相当复杂的)“--stream”命令-line 选项,当输入是(或包含)单个巨大的 JSON 实体时,主要需要它。
然而,仍有几个选择需要做出。主要方法在 中进行了描述,它们是一种多通道方法(对 jq 的 N 或 (N+1) 次调用,其中 N 是输出文件的数量),以及一种只涉及一次调用的方法jq,然后调用 awk
等程序来执行实际的文件分区。每种方法都有其优点和缺点,但如果读取输入文件 N 次是可以接受的,那么第一种方法可能更好。
要估计所需的总计算资源,最好测量 运行ning jq empty input.json
使用的资源
(从您的简短文章来看,您 运行 的内存问题听起来主要是由于文件的解压缩造成的。)
使用 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"')
这不会一次将多条记录(诚然,每条记录可能有多个副本)读取到内存中,因此只要记录的大小合理,它就可以处理任意大的文件。
我有一个名为 input.json
的非常大的文件(压缩后超过 20GB),其中包含一个 JSON 对象流,如下所示:
{
"timestamp": "12345",
"name": "Some name",
"type": "typea"
}
{
"timestamp": "12345",
"name": "Some name",
"type": "typea"
}
{
"timestamp": "12345",
"name": "Some name",
"type": "typeb"
}
我想根据 type
属性 将此文件拆分为多个文件:typea.json
、typeb.json
等,每个文件都包含自己的 json 仅具有匹配类型 属性.
我已经设法为较小的文件解决了这个问题,但是对于如此大的文件,我的 AWS 实例 运行 内存不足。因为我希望降低内存使用率,所以我知道我需要使用 --stream
,但我正在努力寻找如何实现这一点。
cat input.json | jq -c --stream 'select(.[0][0]=="type") | .[1]'
会 return 我得到每个类型属性的值,但是我如何使用它来过滤对象?
如有任何帮助,我们将不胜感激!
假设文件中的 JSON 对象相对较小(none 超过几 MB),您将不需要使用(相当复杂的)“--stream”命令-line 选项,当输入是(或包含)单个巨大的 JSON 实体时,主要需要它。
然而,仍有几个选择需要做出。主要方法在 awk
等程序来执行实际的文件分区。每种方法都有其优点和缺点,但如果读取输入文件 N 次是可以接受的,那么第一种方法可能更好。
要估计所需的总计算资源,最好测量 运行ning jq empty input.json
(从您的简短文章来看,您 运行 的内存问题听起来主要是由于文件的解压缩造成的。)
使用 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"')
这不会一次将多条记录(诚然,每条记录可能有多个副本)读取到内存中,因此只要记录的大小合理,它就可以处理任意大的文件。