简体   繁体   中英

Build nested json data from variable with looping with jq using bash

I'm trying to create a dynamic json data using jq, but I don't know how to write it when involving loop. I can do this in normal bash writing a sequence string

Here is the example code:

#!/bin/bash

# Here I declare 2 arrays just to demonstrate. Each values pairs between `disk_ids` and `volume_ids` cannot have the same value. If one has value, the other must null.

disk_ids=(111 null 222 333)
volume_ids=(null 444 null null)
json_query_data=""

# Now example I want to loop through the above 2 arrays and create json. In real application I already have the above arrays that is looping where I can access using $disk_id and $volume_id.

for disk_id in "${disk_ids[@]}"; do
  for volume_id in "${volume_ids[@]}"; do
    json_query_data=$(jq -n --argjson disk_id "$disk_id" --argjson volume_id "$volume_id" '{
                          devices: {
                                             sda: {"disk_id": $disk_id, "volume_id": $volume_id },
                                             sdb: {"disk_id": $disk_id, "volume_id": $volume_id },
                                             sdc: {"disk_id": $disk_id, "volume_id": $volume_id },
                                             sdd: {"disk_id": $disk_id, "volume_id": $volume_id },
                                          }}')

  done
done

As you can see that is definitely NOT the output that I want, and my code writing logic is not dynamic. The final output should produce the following json when I echo "${json_query_data}" :

{
    devices: {
               sda: {"disk_id": 111, "volume_id": null },
               sdb: {"disk_id": null, "volume_id": 444 },
               sdc: {"disk_id": 222, "volume_id": null },
               sdd: {"disk_id": 333, "volume_id": null },
}}

I have not seen any example online regarding looping with variable when creating json data with jq . Appreciate if someone can help. Thanks.

UPDATE:

I must use for loop inside bash to create the json data. Because the sample array disk_ids and volume_ids that I provided in the code were just example, In real application I already able to access the variable $disk_id and $volume_id for each for loop counter. But how do I use this variables and create the json output that fill up all the data above inside that for loop?

The json example is taken from: linode API here

The looping/mapping can also be accomplished in jq:

#!/bin/bash

disk_ids=(111 null 222 333)
volume_ids=(null 444 null null)

jq -n --arg disk_ids "${disk_ids[*]}" --arg volume_ids "${volume_ids[*]}" '
  [$disk_ids, $volume_ids | . / " " | map(fromjson)]
  | transpose | {devices: with_entries(
      .key |= "sd\([. + 97] | implode)"
      | .value |= {disk_id: first, volume_id: last}
    )}
'

Demo

Or, if you can already provide the letters in the same way:

#!/bin/bash

disk_ids=(111 null 222 333)
volume_ids=(null 444 null null)
letters=(a b c d)

jq -n --arg disk_ids "${disk_ids[*]}" --arg volume_ids "${volume_ids[*]}" --arg letters "${letters[*]}" '
  [$disk_ids, $letters, $volume_ids | . / " " ] | .[0,2] |= map(fromjson)
  | transpose | {devices: with_entries(
      .key = "sd\(.value[1])"
      | .value |= {disk_id: first, volume_id: last}
    )}
'

Demo

Output:

{
  "devices": {
    "sda": {
      "disk_id": 111,
      "volume_id": null
    },
    "sdb": {
      "disk_id": null,
      "volume_id": 444
    },
    "sdc": {
      "disk_id": 222,
      "volume_id": null
    },
    "sdd": {
      "disk_id": 333,
      "volume_id": null
    }
  }
}

UPDATE:

I must use for loop inside bash to create the json data.

If you insist on doing this in a bash loop, how about:

#!/bin/bash

disk_ids=(111 null 222 333)
volume_ids=(null 444 null null)
json='{"devices": {}}'

for i in ${!disk_ids[@]}
do
  json="$(
    jq --argjson disk_id "${disk_ids[$i]}" --argjson volume_id "${volume_ids[$i]}" '
      .devices |= . + {"sd\([length + 97] | implode)": {$disk_id, $volume_id}}
    ' <<< "$json"
  )"
done
echo "$json"

Or, with letters included:

#!/bin/bash

disk_ids=(111 null 222 333)
volume_ids=(null 444 null null)
letters=(a b c d)
json='{"devices": {}}'

for i in ${!disk_ids[@]}
do
  json="$(
    jq --argjson disk_id "${disk_ids[$i]}" --argjson volume_id "${volume_ids[$i]}" --arg letter "${letters[$i]}" '
      .devices["sd\($letter)"] += {$disk_id, $volume_id}
    ' <<< "$json"
  )"
done
echo "$json"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM