0

I'm creating JSON objects from given data. If an JSON object is created it should be appended to an JSON array which is stored in a bash-variable. This batch of JSON objects should later be send via curl.

Code is:

declare -a data=('10.4' '100.23' '20.02');
batch="[]"
for data_object in "${data[@]}"; do
    json=$(jq -n --arg inf $data_object '{data: $inf|tonumber}')
    batch=$(jq '$batch += ["$json"]')
done

The expected result after n loops should be:

[
   {
      "data": 10.4
   },
   {
      "data": 100.23
   },
   {
      "data": 20.02
   }
]

I'm constantly getting this error: jq: error: $batch is not defined at <top-level>, line 1:. How can I solve this?

5
  • batch is used in shell context, it is not automatically imported to jq's Commented Dec 15, 2020 at 18:48
  • Sooo like batch="[$(printf '{"information": %s}\n' "${data[@]}" | paste -sd,)]"? Commented Dec 15, 2020 at 18:49
  • 1
    You don't need any hackery of this sort and can do it all in jq. Post a minimal reproducible example with an input and an exact output needed Commented Dec 15, 2020 at 18:50
  • Where is $information coming from? Commented Dec 15, 2020 at 18:54
  • I updated the post so it makes a bit more sense! Commented Dec 15, 2020 at 18:54

1 Answer 1

5

You could doubtless make your program work by updating batch along the lines of:

batch=$(jq --argjson json "$json" '. + [$json]' <<< "$batch")

However, the approach you're taking seems to be both fragile and astoundingly inefficient. Have you considered accumulating all the items in a file (as a stream of JSON entities), and when they need to be bundled into an array, using jq -s .?

In the particular example given, it would in fact be sufficient to write:

printf "%s\n" "${data[@]}" |
    jq -n '[{data: inputs}]' |
    curl -d @- <OTHER CURL OPTIONS>

That is, you can pipe directly into curl.

Notice that a call to tonumber is unnecessary, whether or not it is known that all the data items are numbers. For reference, tonumber is often used in the idiom (tonumber? // .) in case the string or other input is not numeric.

Another variation

If there were other actions to be taken on each data element that jq cannot readily handle:

batch=$(
   (
       for data_object in "${data[@]}"; do
           jq -n --argjson inf "$data_object" '{data: $inf}'
       done
   ) | jq -s .
)

Here, a call to tonumber is unnecessary, this time thanks to --argjson.

Sign up to request clarification or add additional context in comments.

2 Comments

First of all , thank you for the answer - this is exactly what I needed. I'm always open to learn and find better solutions, especially if mine is actually that bad, so what would be the benefit of storing them in separat files and how would it look like? For further information, the curl that sends the batch is called immediately after the for loop.
@FlixRo, Added alluded jq -s solution to the answer.

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.