使用jq提取多个json对象

hmtdttj4  于 2023-08-08  发布在  其他
关注(0)|答案(2)|浏览(146)

我一直在使用jq成功地从一些相对较大的文件中一次提取一个JSON blob,并将其写入每行一个JSON对象的文件中以进行进一步处理。下面是JSON格式的一个例子:

{
  "date": "2023-07-30",
  "results1":[
    {
      "data": [    
        {"row": [{"key1": "row1", "key2": "row1"}]},
        {"row": [{"key1": "row2", "key2": "row2"}]}
      ]
    },
    {
      "data": [    
        {"row": [{"key1": "row3", "key2": "row3"}]},
        {"row": [{"key1": "row4", "key2": "row4"}]}
      ]
    }
  ],
  "results2":[
    {
      "data": [    
        {"row": [{"key3": "row1", "key4": "row1"}]},
        {"row": [{"key3": "row2", "key4": "row2"}]}
      ]
    },
    {
      "data": [    
        {"row": [{"key3": "row3", "key4": "row3"}]},
        {"row": [{"key3": "row4", "key4": "row4"}]}
      ]
    }
  ]
}

字符串
我目前的方法是运行以下命令并将stdout重定向到一个文件:

jq -rc ".results1[]" my_json.json


这样做很好,但是,看起来jq会将整个文件读入内存,以便提取我感兴趣的块。
问题:
1.当我执行上面的语句时,jq会把整个文件读入内存吗?
1.假设答案是肯定的,有没有一种方法可以在同一次调用中提取results1[]results2[],以避免阅读文件两次?
我使用--stream选项,但它非常慢。我还读到它为了节省内存而牺牲了速度,但内存在这个时候不是问题,所以我宁愿避免使用这个选项。基本上,我需要的是读取上面的json一次,并以JSON行格式输出两个文件。
编辑:(我对输入数据做了一点修改,以显示输出中的差异)
输出文件1:

{"data":[{"row":[{"key1":"row1","key2":"row1"}]},{"row":[{"key1":"row2","key2":"row2"}]}]}
{"data":[{"row":[{"key1":"row3","key2":"row3"}]},{"row":[{"key1":"row4","key2":"row4"}]}]}


输出文件2:

{"data":[{"row":[{"key3":"row1","key4":"row1"}]},{"row":[{"key3":"row2","key4":"row2"}]}]}
{"data":[{"row":[{"key3":"row3","key4":"row3"}]},{"row":[{"key3":"row4","key4":"row4"}]}]}


众所周知,流媒体选项很慢。参见讨论here
我在实现它的尝试遵循了这里的答案。

gijlo24d

gijlo24d1#

jq没有任何文件IO功能,因此不能输出多个文件。
你可以输出每一段数据及其密钥并对其进行后处理:

jq -r '
    to_entries[]
    | select(.key != "date")
    | .key as $k
    | .value[]
    | [$k, @json]
    | @tsv
' my_json.json

字符串
产出

results1    {"data":[{"row":[{"key1":"row1","key2":"row1"}]},{"row":[{"key1":"row2","key2":"row2"}]}]}
results1    {"data":[{"row":[{"key1":"row3","key2":"row3"}]},{"row":[{"key1":"row4","key2":"row4"}]}]}
results2    {"data":[{"row":[{"key3":"row1","key4":"row1"}]},{"row":[{"key3":"row2","key4":"row2"}]}]}
results2    {"data":[{"row":[{"key3":"row3","key4":"row3"}]},{"row":[{"key3":"row4","key4":"row4"}]}]}


于是:

while IFS=$'\t' read -r key json; do
    printf '%s\n' "$json" >> "${key}.jsonl"
done < <(
    jq -r '...' my_json.json
)


或者是

jq -r '...' my_json.json | awk -F '\t' '{print $2 > ($1 ".jsonl")}'

7tofc5zh

7tofc5zh2#

当Bash ≥ 4时,可以通过使用mapfile一次阅读n行来提高处理较大块的能力:

jq -cr '$ARGS.positional[] as $key | .[$key] | $key, length, .[]' input.json \
  --args results1 results2 | while read -r key; read -r len
do mapfile -t -n $len
  printf '%s\n' "${MAPFILE[@]}" > "$key.jsonl"
done

字符串

相关问题