如何在jq中通过变量解析多级键并修改JSON对应值?
问题描述
我有如下JSON文件:
{ "rec_9M": { "id":"000", "rec_E": [ { "id":"111" } , { "id":"222" } ] } }
我需要修改rec_9M.rec_E[].id的值,并且希望编写一个通用的bash函数,可用于修改不同层级路径(如rec_9M.id或rec_9M.rec_E[].files[].id)的JSON值。
我尝试了以下命令:
VAR=rec_9M.rec_E[] NUM=555 echo $(cat config.json) | jq --arg VAR_I $VAR --arg NUM_I $NUM '.[$VAR_I].id = $NUM_I' > config_t.json cat config_t.json
预期输出:
{ "rec_9M": { "id": "000", "rec_E": [ { "id": "555" }, { "id": "555" } ] } }
实际输出:
{ "rec_9M": { "id": "000", "rec_E": [ { "id": "111" }, { "id": "222" } ] }, "rec_9M.rec_E[]": { "id": "555" } }
硬编码键路径时命令可正常执行:
echo $(cat config.json) | jq --arg VAR_I $VAR --arg NUM_I $NUM '.rec_9M.rec_E[].id = $NUM_I' > config_t.json
尝试过VAR_I的各种引号和特殊符号组合(如"($VAR_I)"),结果要么和上述一致,要么出现jq解析错误,请问该如何解决?
解决方案
问题核心是jq无法将字符串形式的路径直接解析为层级访问表达式,.[$VAR_I]会把整个变量值当作单个键名,而非层级路径。以下是两种可行的通用解决方法:
方法1:使用jq内置setpath函数(推荐)
setpath函数支持通过数组形式的路径定位到目标字段,我们可以先将路径字符串转换为jq能识别的数组格式,再传入函数:
直接使用示例
# 把路径拆分为数组,数组索引用[]表示 VAR='["rec_9M", "rec_E", [], "id"]' NUM=555 jq --argjson path "$VAR" --arg value "$NUM" 'setpath($path; $value)' config.json > config_t.json
封装为通用bash函数
如果需要自动处理类似rec_9M.rec_E[].id的路径字符串,可以加一层字符串转换逻辑,把路径转为数组格式:
# 定义通用修改函数:参数1=JSON文件路径,参数2=目标字段路径,参数3=新值 json_set() { local json_file=$1 local path_str=$2 local new_value=$3 # 将路径字符串转换为jq的路径数组格式 local path_arr=$(echo "$path_str" | sed -E ' s/([^\.\[]+)(\[.*?\])?/\1\2/g; s/\./", "/g; s/\[]/","[]/g; s/^/["/; s/$/"]/ ') # 执行修改并覆盖原文件(先写临时文件再替换,避免原文件损坏) jq --argjson path "$path_arr" --arg value "$new_value" 'setpath($path; $value)' "$json_file" > "${json_file}.tmp" && mv "${json_file}.tmp" "$json_file" } # 使用示例 json_set config.json "rec_9M.rec_E[].id" "555" json_set config.json "rec_9M.id" "666"
方法2:动态生成jq表达式(快捷但有风险)
如果能确保路径字符串的安全性(无恶意内容),可以直接将路径拼接到jq表达式中:
VAR=rec_9M.rec_E[].id NUM=555 jq --arg value "$NUM" ".$VAR = \$value" config.json > config_t.json
注意:此方法会直接执行路径对应的jq代码,若路径包含恶意内容可能导致不可预期的结果,仅适合可控场景。
内容的提问来源于stack exchange,提问作者kolt912
相关产品推荐
相关产品推荐

