【问题标题】:Updating JSON object values dynamically and storing them in a new file in BASH动态更新 JSON 对象值并将它们存储在 BASH 中的新文件中
【发布时间】:2018-05-09 12:47:30
【问题描述】:

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

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

这是要替换的 JSON 文件的 sn-p。整个文件太大了,不能在这里发布。 但是键“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 "
       },

“名称”:“Node1”必须替换为“名称”:“Tom-cat”。名称 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

但代码最后只替换了最后一个 $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。

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

【问题讨论】:

  • 首先,-ne 严格用于整数比较;使用!= 进行字符串比较。但是,您应该发布示例输入和预期输出,因为几乎可以肯定有一种方法可以通过调用 jq 来完成此操作,而不是在 bash 中进行迭代。 (在bash 中有一种更简洁的方法来进行迭代,但这里的主题还不足以作为答案发布。{ read; while IFS= read -r new_nodename; do ...; done; } &lt; newhostnames.txt
  • 哦,忽略我的循环评论;现在,你仍然需要i 来匹配你的本地数组和 JSON 数组。
  • 您好,谢谢!我已经编辑了这个问题,我希望这可以更好地说明我想要完成的工作。

标签: json bash jq


【解决方案1】:

这里的目标应该是尽可能少地调用 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

产生:

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

(-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])

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

【讨论】:

  • 您好,感谢您提供此解决方案。但是, slurpfile 命令不会替换 input.json 文件中的 nodes[$i]。它最后在文件中添加了一个新部分:“nodes”:[ null,{“name”:“Tom-cat”},{“name”:“Lucky-worm”},{“name”:“ Wom-bat" }, { "name": "bold-worm" } ] }
【解决方案2】:

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

{
   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

【讨论】:

  • 啊,我明白了!我确实想到文件可能被覆盖了,但我对如何修复它一无所知!
  • 你好 :) 我运行了这段代码,但不幸的是,名称没有更新。我知道没有 JSON 文件很难修复它。
  • 我想我会朝着不同的方向前进,根本不使用 jq。但是在 JSON 文件上使用 awk 或 sed 将“名称”字段替换为文本文件中的字段。我见过一些 sed 或 awk 的例子,以及带有太多 \\// 和 ; 的命令的复杂性。令人生畏。
  • 不,使用jq;一般来说,使用面向行的工具来编辑 JSON 并不安全。您有正确的方法,但您的 jq 过滤器可能只需要调整。
  • 请参阅新部分:“鲁棒化”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-13
  • 1970-01-01
  • 2019-09-21
  • 2012-04-20
  • 1970-01-01
  • 2020-10-15
相关资源
最近更新 更多