动态更新JSON对象值并将它们存储在BASH

时间:2018-05-09 12:47:31

标签: json bash jq

对于BASH,JSON和jq,我是全新的(实习生,实习经验不到两周)。但是我已经被赋予了一个迭代地从数组中替换JSON对象值的任务。我设法编写了以下代码。

我正在尝试将json文件中的键值" name",以递增方式替换为存储在另一个文本文件中的数组中的名称。基本上只是更新节点的名称。

以下是要替换的JSON文件的片段。整个文件太大了,不能在这里发布。 但是关键的路径" name"是" .lab.racks []。nodes [] .name"

    "roles": [
           "bla bla",
           "bla bla"
      ],
      "name": "Node1",
       "power": {
       "address": "10.182.149.145",
       "type": "bla bla",
       "user": "bla bla",
       "pass": "bla bla "
       },

" name":" Node1"必须更换为" name":" Tom-cat"。 Tom-cat的名称是动态生成的,每次运行Metal as a Service(MaaS)脚本时都会更改。这个名字" Tom-cat"并且MaaS生成的所有其他新名称都被删除(使用awk)并存储在文本文件newhostnames.txt中

文本文件看起来像这样

#newhostnames.txt
Tom-cat
Lucky-worm
Wom-bat

所以我们的目标是取代"名称"来自demolabconfig.json的密钥,其名称存储在文本文件中。

"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 []。节点[$ 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

但是代码只替换最后一个$ i的最后一个值,即最后一个key_value对。所有其他前面的节点都保留与原始JSON文件中相同的名称。

如果值已经更新,我尝试使用if语句来中断循环,因此不会从旧的demolabconfig.json文件重新编写。但那也行不通!

if [[ "$OLD_NODENAME" -ne "$NEWNODENAME" ]]; then
    jq ".lab.racks[].nodes[$i].name=env.NEW_NODENAME" demolabconfig.json > newdemolabconfig.json
else
    break
fi

这个if循环是用while循环而不是for循环编写的。这将使用null替换姓氏。

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

1 个答案:

答案 0 :(得分:1)

这里的目标应该是尽可能少地调用jq。因此,我们提出了一个只涉及两个电话的解决方案 到jq,不论&#34;名称的数量&#34;在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

产生

{"nodes":[{"name":"Tom-cat"},{"name":"Lucky-worm"},{"name":"Wom-bat"}]}

( - c命令行选项产生压缩输出。)

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

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

Robustification

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

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

请注意,$ -variable名称可以重复使用。