Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/bash/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
动态更新JSON对象值并将其存储在BASH中的新文件中_Json_Bash_Jq - Fatal编程技术网

动态更新JSON对象值并将其存储在BASH中的新文件中

动态更新JSON对象值并将其存储在BASH中的新文件中,json,bash,jq,Json,Bash,Jq,我是BASH、JSON和jq的新手(实习时间不到两周)。但我被赋予了一项任务,以迭代方式替换数组中的JSON对象值。我已经成功地编写了以下代码 我试图将json文件中的键“name”的值增量替换为另一个文本文件中存储的数组中的名称。基本上只是更新节点的名称 下面是要替换的JSON文件的一个片段。整个文件太大了,无法在这里发布。 但是键“name”的路径是“.lab.racks[].nodes[].name” “名称”:“节点1”必须替换为“名称”:“汤姆猫”。Tom cat的名称是动态生成的,每

我是BASH、JSON和jq的新手(实习时间不到两周)。但我被赋予了一项任务,以迭代方式替换数组中的JSON对象值。我已经成功地编写了以下代码

我试图将json文件中的键“name”的值增量替换为另一个文本文件中存储的数组中的名称。基本上只是更新节点的名称

下面是要替换的JSON文件的一个片段。整个文件太大了,无法在这里发布。 但是键“name”的路径是“.lab.racks[].nodes[].name”

“名称”:“节点1”必须替换为“名称”:“汤姆猫”。Tom cat的名称是动态生成的,每次运行“金属即服务”(MaaS)脚本时都会更改。此名称“Tom cat”和MaaS生成的所有其他新名称被剪切(使用awk)并存储在文本文件newhostnames.txt中

文本文件如下所示

#newhostnames.txt
Tom-cat
Lucky-worm
Wom-bat
因此,目标是用存储在文本文件中的名称替换demolabconfig.json中的“name”键

"name": "Node1" must be replaced as "name":"Tom-cat"
];
.....
.....
],
"name": "Node2" must be replaced as "name":"Lucky-worm"
];
.....
.....
],
"name": "Node3" must be replaced as "name":"Wom-bat"
对键“节点”进行索引:.lab.racks[].nodes[$i].name

守则:

readarray -t array < newhostnames.txt
array=("${array[@]:1}")
array_length=${#array[@]}
for((i=0;i<${array_length};i++));
do
    declare -x  NEW_NODENAME
    OLD_NODENAME=$(jq -r ".lab.racks[].nodes[$i].name" demolabconfig.json)
    echo "$OLD_NODENAME"
    NEW_NODENAME="${array[i]}"
    echo "$NEW_NODENAME"
    jq ".lab.racks[].nodes[$i].name=env.NEW_NODENAME" demolabconfig.json > newdemolabconfig.json
done
这个if循环是用while循环而不是for来编写的。这会将姓氏替换为null


请建议我如何修复此错误并改进代码。谢谢:)

最大的问题是,您在每一步都会使用原始文件的副本覆盖修改后的文件。您希望新文件作为循环下一次迭代的输入:

{
   read   # discard the first line
   readarray -t array
} < newhostnames.txt

cp demolabconfig.json newdemolabconfig.json
input=newdemolabconfig.json

for((i=0;i<${#array[@]};i++));
do
    old_nodename=$(jq -r ".lab.racks[].nodes[$i].name" "$input")
    new_nodename="${array[i]}"
    if [[ $old_nodename == $new_nodename ]]; then
        continue
    fi
    jq ".lab.racks[].nodes[$i].name=env.NEW_NODENAME" "$input" > tmp.json
    mv tmp.json "$input"
done
{
读#丢弃第一行
readarray-t数组
}
这里的目标应该是尽可能少地调用jq。因此,我们提出了一个只需要两次调用的解决方案 对于jq,无论newhostnames.txt中的“名称”数量如何

首先,假设input.json包含以下内容:

{"nodes":[{"name":"old1"},{"name":"old2"},{"name":"old3"}]}
我们还假设以下jq程序位于program.jq文件中:

  reduce range(0; $newhostnames | length) as $i (.;
    .nodes[$i].name = $newhostnames[$i])
然后调用:

jq -c --slurpfile newhostnames <(jq -nR inputs newhostnames.txt ) \
   -f program.jq input.json
(命令行选项-c生成压缩输出。)

如果您的shell不支持上述调用,您可以(例如)将
jq-nR inputs newhostnames.txt的输出放在一个临时文件中

当然,将.txt文件转换为JSON数组或JSON字符串流的辅助任务可以通过其他方式完成

稳健性 如果newhostnames.txt可能包含不需要的空行,那么跳过这些空行的一种方法是在program.jq中添加一行,使其看起来像这样:

($newhostnames | map(select(length>0))) as $newhostnames
| reduce range(0; $newhostnames | length) as $i (.;
      .nodes[$i].name = $newhostnames[$i])

请注意,$-变量名可以重复使用。

首先,
-ne
严格用于整数比较;使用
!=
进行字符串比较。但是,您应该发布一个示例输入和预期输出,因为几乎可以肯定有一种方法可以通过一次调用
jq
,而不是在
bash
中进行迭代。(在
bash
中有一种更简洁的方法来进行迭代,但它的主题还不足以作为答案发布。
{read;while IFS=read-r new_nodename;do…;done;}
。哦,忽略我的循环注释;现在,您仍然需要
i
将您的本地数组与JSON数组相匹配。您好,谢谢!我已经编辑了这个问题,我希望这能更好地说明我正在尝试完成的任务。啊,我明白了!我确实想到文件可能写得太多了,但我没有任何线索知道如何完成修复它!!再次您好:)我运行了这段代码,但不幸的是,名称没有更新。我知道没有JSON文件很难修复它。我想我会走另一条路,根本不使用jq。但是在JSON文件上使用awk或sed来替换“名称”文本文件中的字段。我已经看到了一些sed或awk示例,以及包含太多\\//和;的复杂命令。不,使用
jq
;使用面向行的工具编辑JSON通常不安全。您的方法是正确的,但您的
jq
过滤器可能只需要调整一下。例如请参阅新的部分:“Robustification”您好,感谢您提供了此解决方案。但是,slurpfile命令并没有替换input.json文件中的节点[$i]。它在最后向文件添加了一个新的部分:“nodes:[null,{“name”:“Tom cat”},{“name”:“Lucky worm”},{“name”:“Wom bat”},{“名称”:“粗体蠕虫”}]}
jq -c --slurpfile newhostnames <(jq -nR inputs newhostnames.txt ) \
   -f program.jq input.json
{"nodes":[{"name":"Tom-cat"},{"name":"Lucky-worm"},{"name":"Wom-bat"}]}
($newhostnames | map(select(length>0))) as $newhostnames
| reduce range(0; $newhostnames | length) as $i (.;
      .nodes[$i].name = $newhostnames[$i])