从Bash变量和关联数组创建JSON

3
假设我在bash中声明了以下内容:
mcD="had_a_farm"
eei="eeieeio"
declare -A animals=( ["duck"]="quack_quack" ["cow"]="moo_moo" ["pig"]="oink_oink" )

我需要以下的 JSON:

{
  "oldMcD": "had a farm",
  "eei": "eeieeio",
  "onThisFarm":[
    {
      "duck": "quack_quack",
      "cow": "moo_moo",
      "pig": "oink_oink"
    }
  ]
}

我知道可以使用echo、printf或将文本赋值给变量来实现此操作,但假设animals实际上非常庞大,这样做可能会很繁琐。我也可以循环遍历变量和关联数组,并在进行遍历时创建一个变量。我可以编写任何一种解决方案,但它们似乎都是"错误的方式"。更不用说处理animals中的最后一个项后,我不想再有“,”。

我认为正确的解决方案使用jq,但我很难找到有关如何使用此工具编写jsons(特别是嵌套的jsons)而不是解析它们的文档和示例。

以下是我想出的方法:

jq -n --arg mcD "$mcD" --arg eei "$eei" --arg duck "${animals['duck']}" --arg cow "${animals['cow']}" --arg pig "${animals['pig']}" '{onThisFarm:[ { pig: $pig, cow: $cow, duck: $duck } ], eei: $eei, oldMcD: $mcD }'

产生所需的结果。实际上,我并不真的关心json中键的顺序,但让jq输入反向获取它以获得所需的顺序仍然很烦人。无论如何,这个解决方案很笨拙,写起来并不比声明一个看起来像json的字符串变量更容易(而对于更大的关联数组则是不可能的)。如何以高效、合理的方式构建这样的json?

谢谢!


你可以像我在这个答案中演示的那样使用 $ARGS.positional - https://dev59.com/lrvoa4cB1Zd3GeqP87XI#62468416 - Inian
2个回答

3
假设"animals"数组中没有任何一个键或值包含换行符:
for i in "${!animals[@]}"
do
  printf "%s\n%s\n"  "${i}" "${animals[$i]}"
done | jq -nR --arg oldMcD "$mcD" --arg eei "$eei" '
  def to_o:
    . as $in
    | reduce range(0;length;2) as $i ({}; 
        .[$in[$i]]= $in[$i+1]);

  {$oldMcD, 
   $eei,
   onthisfarm: [inputs] | to_o}
'

注意这个技巧,其中{$x}实际上会扩展为{(x): $x}

使用"\u0000"作为分隔符

如果任何一个键或值包含换行符,你可以调整上面的方法,使用"\u0000"作为分隔符:

for i in "${!animals[@]}"
do
    printf "%s\0%s\0"  "${i}"  "${animals[$i]}"
done | jq -sR --arg oldMcD "$mcD" --arg eei "$eei" '
 def to_o:
   . as $in
   | reduce range(0;length;2) as $i ({};
       .[$in[$i]]= $in[$i+1]);

  {$oldMcD, 
   $eei,
   onthisfarm: split("\u0000") | to_o }
'

注意:以上假定 jq 版本为1.5或更高。


值得一提的是,这些解决方案在 jq-1.3-56 中无法工作,但在我升级到 jq-1.5 后,它们非常好用。谢谢! - Matthew Snyder

1
你可以使用for循环减少关联数组,并将其传递到 jq:
for i in "${!animals[@]}"; do
    echo "$i"
    echo "${animals[$i]}"
done |
jq -n -R --arg mcD "$mcD" --arg eei "$eei" 'reduce inputs as $i ({onThisFarm: [], mcD: $mcD, eei: $eei}; .onThisFarm[0] += {($i): (input | tonumber ? // .)})'

钥匙在哪里?这使得 OnThisFarm 成为一个字符串数组。 - oguz ismail
确实,我没有注意到数组中需要一个对象。谢谢,我已经改变了答案。 - Alloces
1
它无法处理包含换行符和以破折号开头的键/值。但是这仍然是一个好答案,干杯! - oguz ismail
除了上面的评论之外,这个解决方案在 jq-1.3-56 中不起作用,但是在我升级到 jq-1.5 后,它非常适合我的使用情况。谢谢! - Matthew Snyder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接