1

I actually have 2 arrays in bash that contains string values. Something like that :

Array1=(Kevin Paul)
Array2=(OK DANGER)

I would like to create a json with 2 attributes, something like that if possible

{
   "results":[
      {
         "nom":"Kevin",
         "status":"OK"
      },
      {
         "nom":"Paul",
         "status":"Danger"
      }
   ]
}

I read a lot speaking about JQ that i alreay use for my arrays, but no one speak about something like i want :(

One of my test ( that does not respond to what i would like ) :

declare -a test_array
declare -a test_array2
test_array=(apple orange lemon)
test_array2=(DANGER OK WARNING)
echo ${test_array[0]}

echo '['
printf '{"CVEC": "%s", "LVL" : "%s"},\n' "${test_array[@]}, ${test_array2[@]}" | sed '$s/,$//'
echo ']'

Display 

[
{"CVEC": "apple", "LVL" : "orange"},
{"CVEC": "lemon, DANGER", "LVL" : "OK"},
{"CVEC": "WARNING", "LVL" : ""}
]
Kévin
  • 497
  • 10
  • 37
  • The reason to use `jq` is that it ensures valid JSON no matter *what* values are stored in the arrays. A pure `bash` approach requires you to do all the examination of the values to ensure things that need to be escaped are handled properly. – chepner Jun 23 '20 at 12:31
  • Yes sure, but i don't have found anything for bash and build of json that respond to this :( – Kévin Jun 23 '20 at 12:36
  • That's because `bash` doesn't have a built-in way to safely process produce JSON. – chepner Jun 23 '20 at 12:37
  • It seems you are looking for a zip function. eg. https://github.com/stedolan/jq/issues/609 – jhnc Jun 23 '20 at 13:11
  • No sry, not at all what i need – Kévin Jun 23 '20 at 13:11
  • Similar to: https://stackoverflow.com/q/41045659/10971581 , https://stackoverflow.com/q/30721317/10971581 and https://stackoverflow.com/q/17403498/10971581 – jhnc Jun 23 '20 at 13:23
  • Also: https://www.rosettacode.org/wiki/Loop_over_multiple_arrays_simultaneously#UNIX_Shell – jhnc Jun 23 '20 at 13:29
  • @jhnc: looks very deprecated ressource – Gilles Quénot Jun 24 '20 at 14:17
  • @jhnc `zip` would work if you already had JSON arrays; the trouble is safely converting a `bash` array to a JSON array in the first place. – chepner Jun 25 '20 at 16:14

3 Answers3

1

Using a template engine: perl's Template::Toolkit command line tool: tpage:

Files

header:

{
   "results":[

footer:

   ]
}

file.tpl (template):

    {
        "nom": "[% x1 %]",
        "status": "[% x2 %]"
    }[% sep %]

Bash script

#!/bin/bash

arr1=( Kevin Paul  )
arr2=( OK danger )

{
    cat header
    for i in "${!arr1[@]}"; do
        ((i==${#arr1[@]}-1)) && sep='' || sep=','
        tpage --define x1="${arr1[i]}" \
              --define x2="${arr2[i]}" \
              --define sep=$sep file.tpl
    done
    cat footer
} | tee file.json

Validation

$ jq . file.json
{
  "results": [
    {
      "nom": "Kevin",
      "status": "OK"
    },
    {
      "nom": "Paul",
      "status": "danger"
    }
  ]
}

Package

For debian and debian like:

apt install libtemplate-perl

Via CPAN:

cpan -i Template::Toolkit

Check http://www.template-toolkit.org/docs/tools/tpage.html

Gilles Quénot
  • 173,512
  • 41
  • 224
  • 223
0

One way:

paste <(printf "%s\n" "${Array1[@]}") <(printf "%s\n" "${Array2[@]}") |
    jq -nRc '{ results: [inputs] | map(split("\t") | { nom: .[0], status: .[1] }) }'

produces

{"results":[{"nom":"Kevin","status":"OK"},{"nom":"Paul","status":"DANGER"}]}

This assumes that the elements of your arrays do not have tabs or newlines in them. It uses paste to generate pairs of corresponding array elements, separated by tabs, one pair per line, and then uses jq to create the JSON output from that.

Shawn
  • 47,241
  • 3
  • 26
  • 60
0

If the objective is to stick with a non-jq solution - and chepner's comments about needing to validate the array entries is not an issue for this situation - one idea would be to loop through the array indices.

Test data:

$ declare -a test_array=(apple orange lemon)
$ typeset -p test_array
declare -a test_array=([0]="apple" [1]="orange" [2]="lemon")

$ declare -a test_array2=(DANGER OK WARNING)
$ typeset -p test_array2
declare -a test_array2=([0]="DANGER" [1]="OK" [2]="WARNING")

A simple loop through the indices (0,1,2):

sfx=','                                                   # default printf suffix is a comma

for (( i=0 ; i<${#test_array[@]} ; i++ ))
do
    (( ${i} == ( ${#test_array[@]} - 1 ) )) && sfx=''     # clear suffix for last pass through loop

    printf '{"CVEC": "%s", "LVL" : "%s"}%s\n' "${test_array[${i}]}" "${test_array2[${i}]}" "${sfx}"
done

Which generates the following:

{"CVEC": "apple", "LVL" : "DANGER"},
{"CVEC": "orange", "LVL" : "OK"},
{"CVEC": "lemon", "LVL" : "WARNING"}
markp-fuso
  • 28,790
  • 4
  • 16
  • 36