jq:在从 json 文件和 bash stdout 读取输入时插入新对象
jq: insert new objects while reading inputs from json file and bash stdout
我想使用 bash 生成的 uuid 在 json 个对象之间插入新的 json 个对象。
输入json文件test.json
{"name":"a","type":1}
{"name":"b","type":2}
{"name":"c","type":3}
输入bash命令uuidgen -r
目标输出json
{"id": "7e3ca7b0-48f1-41fe-9a19-092a62cba0dc"}
{"name":"a","type":1}
{"id": "3f793fdd-ec3b-4306-8153-12f3f9faf2c1"}
{"name":"b","type":2}
{"id": "cbcd759a-37e7-4da7-b7fe-7572f474ec31"}
{"name":"c","type":3}
用于插入新对象的基本 jq 程序
jq -c '{"id"}, .' test.json
输出json
{"id":null}
{"name":"a","type":1}
{"id":null}
{"name":"b","type":2}
{"id":null}
{"name":"c","type":3}
jq 程序插入从 bash:
生成的 uuid
jq -c '{"id" | input}, .' test.json < <(uuidgen)
不确定如何处理两个输入,bash命令用于在新对象中创建值,以及要转换的输入文件(新对象插入每个对象之间)。
我想处理大小不超过 json 的文件,每个文件最多几千兆字节。
非常希望得到一些设计良好的解决方案的帮助,这些解决方案可以针对大型文件扩展并快速高效地执行操作。
提前致谢。
如果输入文件已经是格式正确的 JSONL,那么一个简单的 bash 解决方案是:
while IFS= read -r line; do
printf "{\"id\": \"%s\"}\n" $(uuidgen)
printf '%s\n' "$line"
done < test.json
如果 test.json 非常大并且已知是有效的 JSONL,这可能是最好的简单解决方案。
如果输入文件不是 JSONL,那么您仍然可以通过管道输入 jq -c . test.json
使用上述方法。如果“读取”太慢,您仍然可以使用上述文本处理方法 awk
。
郑重声明,按照您所想的方式,可以按如下方式构造一个 jq 单调用解决方案:
jq -n -c -R --slurpfile objects test.json '
$objects[] | {"id": input}, .' <(while true ; do uuidgen ; done)
显然你不能“吞噬”无限的 uuidgen 值流;也许不太明显,如果您只是简单地在流中进行管道传输,该过程将挂起。
自 以来,我将尝试使用 Python 更有效地执行此操作,仍然包装以便可以在 shell 脚本中调用它。
这假设您的输入是 JSONL,每行一个文档。如果不是,请考虑先通过 jq -c .
管道,然后再管道进入下方。
#!/usr/bin/env bash
py_prog=$(cat <<'EOF'
import json, sys, uuid
for line in sys.stdin:
print(json.dumps({"id": str(uuid.uuid4())}))
sys.stdout.write(line)
EOF
)
python -c "$py_prog" <in.json >out.json
如果事先不知道输入是有效的 JSONL,
以下 bash+jq 解决方案之一可能有意义
因为计算对象数量的开销相对较小。
如果输入足够小以适合内存,您可以采用简单的解决方案:
n=$(jq -n 'reduce inputs as $in (0; .+1)' test.json)
for ((i=0; i < $n; i++)); do uuidgen ; done |
jq -n -c -R --slurpfile objects test.json '
$objects[] | {"id": input}, .'
否则,也就是说,如果输入非常大,那么可以避免像这样吞掉它:
n=$(jq -n 'reduce inputs as $in (0; .+1)' test.json)
jq -nc --rawfile ids <(for ((i=0; i < $n; i++)); do uuidgen ; done) '
$ids | split("\n") as $ids
| foreach inputs as $in (-1; .+1; {id: $ids[.]}, $in)
' test.json
这是另一种方法,其中 jq
将输入作为原始字符串处理,已经由 bash 的单独副本混合。
while IFS= read -r line; do
uuidgen
printf '%s\n' "$line"
done | jq -Rrc '({ "id": . }, input)'
它仍然具有每个输入行调用一次 uuidgen
的所有性能开销(加上一些额外的开销,因为 bash 的 read
一次操作一个字节)--但它在固定数量的内存中运行而不需要 Python.
我想使用 bash 生成的 uuid 在 json 个对象之间插入新的 json 个对象。
输入json文件test.json
{"name":"a","type":1}
{"name":"b","type":2}
{"name":"c","type":3}
输入bash命令uuidgen -r
目标输出json
{"id": "7e3ca7b0-48f1-41fe-9a19-092a62cba0dc"}
{"name":"a","type":1}
{"id": "3f793fdd-ec3b-4306-8153-12f3f9faf2c1"}
{"name":"b","type":2}
{"id": "cbcd759a-37e7-4da7-b7fe-7572f474ec31"}
{"name":"c","type":3}
用于插入新对象的基本 jq 程序
jq -c '{"id"}, .' test.json
输出json
{"id":null}
{"name":"a","type":1}
{"id":null}
{"name":"b","type":2}
{"id":null}
{"name":"c","type":3}
jq 程序插入从 bash:
生成的 uuidjq -c '{"id" | input}, .' test.json < <(uuidgen)
不确定如何处理两个输入,bash命令用于在新对象中创建值,以及要转换的输入文件(新对象插入每个对象之间)。
我想处理大小不超过 json 的文件,每个文件最多几千兆字节。
非常希望得到一些设计良好的解决方案的帮助,这些解决方案可以针对大型文件扩展并快速高效地执行操作。
提前致谢。
如果输入文件已经是格式正确的 JSONL,那么一个简单的 bash 解决方案是:
while IFS= read -r line; do
printf "{\"id\": \"%s\"}\n" $(uuidgen)
printf '%s\n' "$line"
done < test.json
如果 test.json 非常大并且已知是有效的 JSONL,这可能是最好的简单解决方案。
如果输入文件不是 JSONL,那么您仍然可以通过管道输入 jq -c . test.json
使用上述方法。如果“读取”太慢,您仍然可以使用上述文本处理方法 awk
。
郑重声明,按照您所想的方式,可以按如下方式构造一个 jq 单调用解决方案:
jq -n -c -R --slurpfile objects test.json '
$objects[] | {"id": input}, .' <(while true ; do uuidgen ; done)
显然你不能“吞噬”无限的 uuidgen 值流;也许不太明显,如果您只是简单地在流中进行管道传输,该过程将挂起。
自
这假设您的输入是 JSONL,每行一个文档。如果不是,请考虑先通过 jq -c .
管道,然后再管道进入下方。
#!/usr/bin/env bash
py_prog=$(cat <<'EOF'
import json, sys, uuid
for line in sys.stdin:
print(json.dumps({"id": str(uuid.uuid4())}))
sys.stdout.write(line)
EOF
)
python -c "$py_prog" <in.json >out.json
如果事先不知道输入是有效的 JSONL, 以下 bash+jq 解决方案之一可能有意义 因为计算对象数量的开销相对较小。
如果输入足够小以适合内存,您可以采用简单的解决方案:
n=$(jq -n 'reduce inputs as $in (0; .+1)' test.json)
for ((i=0; i < $n; i++)); do uuidgen ; done |
jq -n -c -R --slurpfile objects test.json '
$objects[] | {"id": input}, .'
否则,也就是说,如果输入非常大,那么可以避免像这样吞掉它:
n=$(jq -n 'reduce inputs as $in (0; .+1)' test.json)
jq -nc --rawfile ids <(for ((i=0; i < $n; i++)); do uuidgen ; done) '
$ids | split("\n") as $ids
| foreach inputs as $in (-1; .+1; {id: $ids[.]}, $in)
' test.json
这是另一种方法,其中 jq
将输入作为原始字符串处理,已经由 bash 的单独副本混合。
while IFS= read -r line; do
uuidgen
printf '%s\n' "$line"
done | jq -Rrc '({ "id": . }, input)'
它仍然具有每个输入行调用一次 uuidgen
的所有性能开销(加上一些额外的开销,因为 bash 的 read
一次操作一个字节)--但它在固定数量的内存中运行而不需要 Python.