1

I do not seem to be able to find an answer, but have seen enough to know there is likely a better way of doing what I want to do.

Problem: I have a bash array. For each element in the bash array, I want to update a JSON array.

The JSON looks like the below. I am wanting to update the fruit array.

  "foods": {
    "perishable": {
      "fruit": []

I'll get an array of length n, for example:

fruit_array=("banana" "orange")

It should look something like this:

  "foods": {
    "perishable": {
      "fruit": [
        { 
          "001": {
            "002": "banana"
          }
        },
        { 
          "001": {
            "002": "orange"
          }
        }
      ]

Is there a nice way of doing this? At the moment I am trying the below:

#!/bin/bash

fruit_array=("banana" "orange")

for fruit in "${fruit_array[@]}"; do
   jq \
   --arg fruit $fruit \
   '.foods.perishables.fruit += [{"001": {"002": $fruit}}]' \
   template.json > template_with_fruit.json
done

This doesn't work for the obvious reason that the template is being re-read, but I have messed around to get it consuming the output of the previous iteration and nothing comes out at the end. I am only able to update the template once.

However, I know this seems a little dodgy and suspect there is a cleaner, more jq way.

A previous - aborted - attempt went something like this:

jq \
--argjson fruit "$(printf '{"001": {"002": "%s"}}\n' \"${fruit_array[@]}\" | jq -nR '[inputs]')" \
'.foods.perishables.fruit += $fruit' \

Which produced a escaped string which I couldn't do anything with, but at least hinted that there might be a neater solution to the standard bash loop.

I am missing something.

Any help would, as always, be appreciated.

2
  • How does the keying work? "001" --> Do you want 2 leading 0? Or must it be 3 long? Commented Sep 30, 2022 at 9:40
  • Your JSON template seems to be broken; I'm guessing it should be {"foods": {"perishable": {"fruit": []}}} Commented Sep 30, 2022 at 9:41

3 Answers 3

6

JQ can do all that on its own; you don't need a loop or anything.

jq '.foods.perishable.fruit += (
  $ARGS.positional
  | map({"001": {"002": .}})
)' template.json --args "${fruit_array[@]}" >template_with_fruit.json
Sign up to request clarification or add additional context in comments.

Comments

1

If you pass your array as a space delimited string, you can use JQ like so:

jq --arg fruits "$fruit_array" \
    '.foods.perishable.fruit |= ($fruits | split(" ") | map({ "001": { "002": . } }))' input

{
  "foods": {
    "perishable": {
      "fruit": [
        {
          "001": {
            "002": "banana"
          }
        },
        {
          "001": {
            "002": "orange"
          }
        }
      ]
    }
  }
}

1 Comment

In bash, $fruit_array is the same as ${fruit_array[0]} i.e. only the first element of the array. You would want --arg fruits "${fruit_array[*]}" here
-1

Create whole json string at once with printf:

fruit_array=("banana" "orange")
printf -v fruits '{"001": {"002": %s}},' "${fruit_array[@]}"
$ echo $fruits
{"001": {"002": banana}},{"001": {"002": orange}},

Then add it to your template removing last comma:

$ echo ${fruits%,}
{"001": {"002": banana}},{"001": {"002": orange}}

"Numbers" could also be set like this:

fruit_array=("1 2 banana" "3 4 orange")
printf '{"%.3d": {"%.3d": %s}},' ${fruit_array[@]}
{"001": {"002": banana}},{"003": {"004": orange}},

1 Comment

That will break for odd fruits like 'fruit with"quote' -- better to let jq handle all the input. Plus, you're missing quotes around the fruit strings in the temporary JSON.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.