You can do this:

X=("hello world" "goodnight moon")
printf '%s\n' "${X[@]}" | jq -R . | jq -s .

output

[
  "hello world",
  "goodnight moon"
]
Answer from kev on Stack Overflow
🌐
Baeldung
baeldung.com › home › scripting › build a json string with bash variables
Build a JSON String With Bash Variables | Baeldung on Linux
August 12, 2024 - However, this approach is incompatible with values that aren’t null or strings, such as booleans, numbers, and arrays. In many real-world scenarios, we may need to handle arrays of complex objects rather than simple arrays or primitive types. For example, we may need to manage a list of users, where each user has a name, age, and a list of skills. We want to convert this list to a JSON array of objects: #!/bin/bash # Define arrays of user attributes names=("Alice" "Bob" "Carol") ages=(29 34 28) declare -A skills skills["Alice"]="Bash Python" skills["Bob"]="JavaScript HTML CSS" skills["Carol"
Top answer
1 of 4
4

Simply use printf to format the output into JSON

First, you have a very blatant typo in this part of your code right here:

echo "${array[3]:$var-3:4}

Note there is no closing straight quote: ". Fixed it in the rewrite I did below:

But more to the point, doing something like this (using printf) as suggested in this StackOverflow answer. Tested and works in CentOS 7.

#!/bin/bash

readarray -t array <<< "$(df -h)";
var=$(echo "${array[1]}"| grep -aob '%' | grep -oE '[0-9]+');
df_output="${array[3]:$var-3:4}";

manufacturer=$(cat /sys/class/dmi/id/chassis_vendor);
product_name=$(cat /sys/class/dmi/id/product_name);
version=$(cat /sys/class/dmi/id/bios_version);
serial_number=$(cat /sys/class/dmi/id/product_serial);
hostname=$(hostname);
operating_system=$(hostnamectl | grep "Operating System" | cut -d ' ' -f5-);
architecture=$(arch);
processor_name=$(awk -F':' '/^model name/ {print $2}' /proc/cpuinfo | uniq | sed -e 's/^[ \t]*//');
memory$(dmidecode -t 17 | grep "Size.*MB" | awk '{s+=$2} END {print s / 1024"GB"}');
hdd_model=$(cat /sys/block/sda/device/model);
system_main_ip=$(hostname -I);

printf '{"manufacturer":"%s","product_name":"%s","version":"%s","serial_number":"%s","hostname":"%s","operating_system":"%s","architecture":"%s","processor_name":"%s","memory":"%s","hdd_model":"%s","system_main_ip":"%s"}' "$manufacturer" "$product_name" "$version" "$serial_number" "$hostname" "$operating_system" "$architecture" "$processor_name" "$memory" "$hdd_model" "$system_main_ip"

The output I get is this:

{"manufacturer":"Oracle Corporation","product_name":"VirtualBox","version":"VirtualBox","serial_number":"","hostname":"sandbox-centos-7","operating_system":"CentOS Linux 7 (Core)","architecture":"x86_64","processor_name":"Intel(R) Core(TM) i5-1030NG7 CPU @ 1.10GHz","memory":"","hdd_model":"VBOX HARDDISK   ","system_main_ip":"10.0.2.15 192.168.56.20 "}

And if you have jq installed, you can pipe the output of the shell script to jq to “pretty print” the output into some human readable format. Like let’s say your script is named my_script.sh, just pipe it to jq like this:

./my_script.sh | jq

And the output would look like this:

{
  "manufacturer": "Oracle Corporation",
  "product_name": "VirtualBox",
  "version": "VirtualBox",
  "serial_number": "",
  "hostname": "sandbox-centos-7",
  "operating_system": "CentOS Linux 7 (Core)",
  "architecture": "x86_64",
  "processor_name": "Intel(R) Core(TM) i5-1030NG7 CPU @ 1.10GHz",
  "memory": "",
  "hdd_model": "VBOX HARDDISK   ",
  "system_main_ip": "10.0.2.15 192.168.56.20 "
}
2 of 4
1

The following programs can output json:

lshw:

lshw -json

smartmontools v7+:

smartctl --json --all /dev/sda

lsblk:

lsblk --json

lsipc:

lsipc --json

sfdisk

sfdisk --json
🌐
Ingernet
ingernet.github.io › bash › jq › json › 2020 › 04 › 16 › json-array-bash-array.html
Converting a JSON array to a bash array
April 16, 2020 - # using gcloud output as a source because why not use the hardest shit possible bork=$(gcloud --project=<project-id> container images list-tags us.gcr.io/<project-id>/<image-name> --filter='tags:DEPLOYED' --format=json | jq '.[0].tags') echo $bork [ "260", "61a1d7aef75421f5c209c42304716ba44e86ab7a", "DEPLOYED.2019-11-12T17.04.37.772145800Z", "DEPLOYED.2019-11-13T00.00.29.525908800Z" ] # ^ output is obviously not a bash array # strip out all the things you don't want - square brackets and commas borkstring=$(echo $bork | sed -e 's/\[ //g' -e 's/\ ]//g' -e 's/\,//g') arr=( $borkstring ) echo $arr ( "260" "61a1d7aef75421f5c209c42304716ba44e86ab7a" "DEPLOYED.2019-11-12T17.04.37.772145800Z" "DEPLOYED.2019-11-13T00.00.29.525908800Z" ) # ^ now THAT is a bash array
🌐
Reddit
reddit.com › r/bash › convert bash array to json with jq
r/bash on Reddit: Convert bash array to JSON with jq
May 2, 2020 -

I am writing a bash script for an Alfred Workflow. In this script I get a list (separated with newlines) from a command that I want to convert into a specific JSON format.

I tried storing the output into an array and parsing that in jq like that:

Command output:

$ piactl get regions

auto
france
netherlands

Create array:

$ IFS=$'\n'
$ regions=($(piactl get regions)) 
$ echo "${regions[@]}"

auto france netherlands

Parse to jq

$ jq -n --arg inarr "${regions}" '{ items: $inarr | split("\n") }'

{
  "items": [
    "auto"
  ]
}

jq only outputs one item of the array and I don't know how to shape the JSON like shown in the wanted output below.

Wanted output:

{"items": [
    {
        "title": "auto",
        "arg": "auto",
        "icon": {
            "path": "icon.png"
        }
    },
    {
        "title": "france",
        "arg": "france",
        "icon": {
            "path": "icon.png"
        }
    },
    {
        "title": "netherlands",
        "arg": "netherlands",
        "icon": {
            "path": "icon.png"
        }
    },
]}

Can somebody help me craft the correct jq arguments for this task?

🌐
Linux.org
linux.org › home › forums › general linux forums › general linux topics
Convert String to an Array in JSON file using Shell scripting | Linux.org
November 27, 2019 - I want all the IP address strings to be converted into an array. For example "10.38.32.202" has to be converted to ["10.38.32.202"] everywhere in the JSON. There are multiple IPs in a JSON I am pasting one sample object from the JSON. But the IPs already in an Array should not be altered. {"network_ip_v4_address": "10.38.32.202","mac_address": "A0:12:34:45","network_ip_v4_address":["10.38.61.1","10.38.32.1"]} ... I have a Bash script which gets data in JSON, I want to be able to convert the JSON into an accessible structure - array / list / or other model which would be easy to parse the nested data.
🌐
GitHub
gist.github.com › oresoftware › a4e3948b0ce9c22752c759d7e694c9ab
Convert array to JSON in Bash shell · GitHub
Convert array to JSON in Bash shell · Raw · array2json.sh · This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Find elsewhere
🌐
Reddit
reddit.com › r/bash › issue making json string
r/bash on Reddit: Issue making JSON string
November 9, 2023 -

So I need to make a pure JSON string using a variable, and I'm bashing (no pun intended) my head against the wall trying to get the result I want:

The desired result looks like this:

'{"app":"app-version.tar","db":"db-version.tar"}'

The variable I have would contain the version info (so like, $VERSION), and I CANNOT get a proper JSON out of something like

'{"app":"app-'$VERSION'.tar","db":"db-'$VERSION'.tar"}'

Anyone able to help?

Edit: I also need to echo the EXACT string being generated at some earlier point as well, for making sure everything is correct.

Top answer
1 of 4
2
'{"app":"app-'$VERSION'.tar","db":"db-'$VERSION'.tar"}' That should work fine, although you really should put double quotes around the variable expansions (because you should always put double quotes around variable expansions): $ VERSION=1.2.3 $ echo '{"app":"app-'"$VERSION"'.tar","db":"db-'"$VERSION"'.tar"}' {"app":"app-1.2.3.tar","db":"db-1.2.3.tar"} or you could use double quotes around the whole thing and need fewer, but then you have to backslash the literal ones: $ echo "{\"app\":\"app-$VERSION.tar\",\"db\":\"db-$VERSION.tar\"}" {"app":"app-1.2.3.tar","db":"db-1.2.3.tar"} Either of those works perfectly fine. If it's not working for you, you're doing something else wrong with the way you pass the value along. I am personally a fan of the jo ("JSON output") command for generating well-formed JSON using a more shell-friendly syntax: $ jo app="app-$VERSION.tar" db="db-$VERSION.tar" {"app":"app-1.2.3.tar","db":"db-1.2.3.tar"} Though as others have said, you can also use jq, which you probably already have installed: $ jq -cn --arg v "$VERSION" '.app = "app-"+$v|.db="db-"+$v' {"app":"app-1.2.3","db":"db-1.2.3"} In either of the last two examples the JSON string is output by the command, so to get it into a variable you would use command substitution, e.g. either of the commands below: json=$(jo app="app-$VERSION.tar" db="db-$VERSION.tar") json=$(jq -cn --arg v "$VERSION" '.app = "app-"+$v|.db="db-"+$v')
2 of 4
2
You should not leave the variable refs unquoted, as the shell will split them on whitespace. Personally I'd rather do something like: JSON_BODY="$(jq --arg version "${VERSION}" '{ "app": "app-" + $version + ".tar", "db": "db-" + $version + ".tar" }')" and use ${JSON_BODY} as the JSON string.
🌐
Unix.com
unix.com › unix for beginners q & a
Convert String to an Array using shell scripting in JSON file. - UNIX for Beginners Q & A - Unix Linux Community
November 27, 2019 - This is the sample json I have pasted here. I want all the IP address strings to be converted into an array. For example “10.38.32.202” has to be converted to “10.38.32.202”] everywhere in the JSON. There are multiple IP…
Top answer
1 of 1
4

Here is a Unit Tested alternate implementation.

It does not use eval, but jq's builtin @sh filter extensively to safely convert JSON data to properly escaped shell strings.

For the conversion back into JSON, it passes the path elements as shell arguments to jq, so there is no need to escape and reprocess it.

The conversion back to JSON also takes care of recreating numerical indexes. To make Unit Test possible, it uses jq's -S option to sort keys and -c compact output to minimize length of strings to compare.

#!/usr/bin/env bash

read -rd '' obj2bash <<'JQ'
# Collects each leaf paths into a $path array
leaf_paths as $path | {
  # Path array into bash string of space-delimited bash strings
  "key": ($path | @sh) | @sh,
  # Value into a bash sting
  "value": getpath($path) | @sh
} | (
  # Construct bash associative array element delcaration
  "[" + .key + "]=" + .value
)
JQ

read -rd '' bash2obj <<'JQ'
# Json object is passed as variable j
$j |
# Each path element is passed as positional argument array
setpath(
  [
    # Convert back path element to number if it is a number
    $ARGS.positional[] | tonumber? // .
  ];
  # Value is passed as variable v
  $v
)
JQ

json2assoc() {
  jq -r "$obj2bash"
}

assoc2json() {
  # shellcheck disable=SC2034 # shellcheck is nameref illiterate
  local -n ass=${1:?} && [ "${assoc@a}" = A ] || exit 2
  local -- json='{}'
  local -- k

  for k in "${!ass[@]}"; do
    local -a path="($k)"
    local -- v="${ass[$k]}"
    json=$(jq -ncS --argjson j "$json" --arg v "$v" "$bash2obj" --args "${path[@]}")
  done
  printf '%s\n' "$json"
}

jsonFile=${1:?}

# shellcheck disable=SC2155 # Dynamically built assoc declaration
declare -A assoc="($(json2assoc < "$jsonFile"))"

# Unit Test
generatedJson=$(assoc2json assoc)
inputJson=$(jq -cS . < "$jsonFile")

printf 'Input:\n%s\n' "$inputJson"
printf 'Generated:\n%s\n' "$generatedJson"

# Simple assertion function
assert() { eval "$*" || printf 'Assertion fail $?=%d: %s\n' "$?" "$*" >&2;}

assert '[ "$generatedJson" = "$inputJson" ]' || exit 1

Another Variant that merge the path array with / into a bash string to make it more practicable to use, reference and navigate those keys within Bash than with serialized Bash array declarations. It adds a constraint that JSON keys must not contain /:

#!/usr/bin/env bash

read -rd '' obj2bash <<'JQ'
leaf_paths as $path | {
  "key": $path | join("/") | @sh,
  "value": getpath($path) | @sh
} | ("[" + .key + "]=" + .value)
JQ

# shellcheck disable=SC2016 # Not shell variables
bash2obj='$j | setpath([$k | split("/")[] | tonumber? // .]; $v)'

json2assoc() { jq -r "$obj2bash";}

assoc2json() {
  # shellcheck disable=SC2034 # shellcheck is nameref illiterate
  local -n ass=${1:?} && [ "${assoc@a}" = A ] || exit 2
  local -- json='{}' k

  for k in "${!ass[@]}"; do
    json=$(jq -cnS --argjson j "$json" \
      --arg k "$k" --arg v "${ass[$k]}" "$bash2obj")
  done
  printf '%s\n' "$json"
}

jsonFile=${1:?}

# shellcheck disable=SC2155 # Dynamically built assoc declaration
declare -A assoc="($(json2assoc < "$jsonFile"))"

# Debug generated associative array
declare -p assoc

# Unit Test
generatedJson=$(assoc2json assoc)
inputJson=$(jq -cS . < "$jsonFile")

# Simple assertion function
assert() { eval "$*" || printf 'Assertion fail $?=%d: %s\n' "$?" "$*" >&2;}

assert '[ "$generatedJson" = "$inputJson" ]' || exit 1
🌐
LinuxSimply
linuxsimply.com › home › bash scripting tutorial › a complete guide to bash array › array operations in bash › how to convert a json array into bash array [5 methods]
How to Convert a JSON Array into Bash Array [5 Methods] - LinuxSimply
March 17, 2024 - The jq command extracts each element of the JSON array in converting such an array into Bash arrays by using the .[] notation within the full syntax echo "$json_array" | jq -r '.[]' and outputs the elements as separate lines.
Top answer
1 of 5
5

I'd get jq to output the results line-wise. Then use the bash mapfile command to read the lines into an array

mapfile -t correct < <(jq -r '.results[] | .correct_answer' <<<"$quiz")
declare -p correct
declare -a correct=([0]="Amazon" [1]="60 lbs" [2]="True")

For the incorrect answers, since bash does not have multi-dimensional arrays, I'd get jq to output the array of incorrect answers as CSV:

mapfile -t incorrect < <(jq -r '.results[] | .incorrect_answers | @csv' <<<"$quiz")
declare -p incorrect
declare -a incorrect=([0]="\"Netflix\",\"BBC\",\"CCTV\"" [1]="\"30 lbs\",\"50 lbs\",\"70 lbs\"" [2]="\"False\"")

Then:

$ echo "q $i: ans=${correct[i]}; wrong=${incorrect[i]}"
q 1: ans=60 lbs; wrong="30 lbs","50 lbs","70 lbs"

Documentation:

  • Process Substitution
  • Here Strings

Assuming you're interacting with the user:

i=0
read -p "Answer to question $i: " answer

if [[ $answer == "${correct[i]}" ]]; then
    echo Correct
elif [[ ${incorrect[i]} == *"\"$answer\""* ]]; then
    echo Incorrect
else
    echo Invalid answer
fi

Keep in mind that within [[...]] the == operator is not a string equality operator, it is a pattern matching operator.

  • the first test is a straightforward string comparison
  • the second test sees if the CVS string holding the incorrect answers contains the answer in double quotes
2 of 5
2

Extending @RomanPerekhrest's answer (go upvote it now):

mapfile -t answers < <(jq -r '.results[] | [.correct_answer] + .incorrect_answers | @sh' <<<"$quiz")
declare -p answers
declare -a answers=([0]="'Amazon' 'Netflix' 'BBC' 'CCTV'" [1]="'60 lbs' '30 lbs' '50 lbs' '70 lbs'" [2]="'True' 'False'")

Then, you can use something like this

for i in "${!answers[@]}"; do
    declare -a "this_answers=( ${answers[i]} )"
    echo $i
    printf " > %s\n" "${this_answers[@]}"
done
0
 > Amazon
 > Netflix
 > BBC
 > CCTV
1
 > 60 lbs
 > 30 lbs
 > 50 lbs
 > 70 lbs
2
 > True
 > False
🌐
Reddit
reddit.com › r/bash › convert json array to bash array
r/bash on Reddit: Convert JSON array to bash array
December 25, 2024 -

Hi guys,

I am a linux noob and am trying to write a script to extract info from a mkv file using mkvmerge but am not able to convert the target json script to a bash array. I have tried a number of solutions from stack overflow but with no success.

here are some of my attempts

dir="/mnt/Anime/Series/KonoSuba/Season 2/[Nep_Blanc] KonoSuba II 10 .mkv"
*********************************************************************************
ARRAY_SIZE=$(mkvmerge -J  "$dir" | jq '.tracks | length')
count=0
arr=()

while [ $count -lt $ARRAY_SIZE ];
    do
        arr+=($(mkvmerge -J  "$dir" | jq '.tracks'[$count]))
        ((count++))
done
*********************************************************************************
readarray -t test_array < <(mkvmerge -J  "$dir" | jq '.tracks')
for element in "${test_array[@]}";
    do
        echo "$element"
done

*********************************************************************************
array=($(mkvmerge -J  "$dir" | jq '.tracks' | sed -e 's/^\[/(/' -e 's/\]$/)/'))

but the echo prints out lines instead of the specific objects.

Though now it is helpling me with my python, originally the project was to help me learn bash scripting. I would really like to have a bash implementation so any help overcoming this roadblock would be appreciated.

Top answer
1 of 4
22

If you really cannot use a proper JSON parser such as jq[1] , try an awk-based solution:

Bash 4.x:

readarray -t values < <(awk -F\" 'NF>=3 {print $4}' myfile.json)

Bash 3.x:

IFS=$'\n' read -d '' -ra values < <(awk -F\" 'NF>=3 {print $4}' myfile.json)

This stores all property values in Bash array ${values[@]}, which you can inspect with
declare -p values.

These solutions have limitations:

  • each property must be on its own line,
  • all values must be double-quoted,
  • embedded escaped double quotes are not supported.

All these limitations reinforce the recommendation to use a proper JSON parser.


Note: The following alternative solutions use the Bash 4.x+ readarray -t values command, but they also work with the Bash 3.x alternative, IFS=$'\n' read -d '' -ra values.

grep + cut combination: A single grep command won't do (unless you use GNU grep - see below), but adding cut helps:

readarray -t values < <(grep '"' myfile.json | cut -d '"' -f4)

GNU grep: Using -P to support PCREs, which support \K to drop everything matched so far (a more flexible alternative to a look-behind assertion) as well as look-ahead assertions ((?=...)):

readarray -t values < <(grep -Po ':\s*"\K.+(?="\s*,?\s*$)' myfile.json)

Finally, here's a pure Bash (3.x+) solution:

What makes this a viable alternative in terms of performance is that no external utilities are called in each loop iteration; however, for larger input files, a solution based on external utilities will be much faster.

#!/usr/bin/env bash

declare -a values # declare the array                                                                                                                                                                  

# Read each line and use regex parsing (with Bash's `=~` operator)
# to extract the value.
while read -r line; do
  # Extract the value from between the double quotes
  # and add it to the array.
  [[ $line =~ :[[:blank:]]+\"(.*)\" ]] && values+=( "${BASH_REMATCH[1]}" )
done < myfile.json                                                                                                                                          

declare -p values # print the array

[1] Here's what a robust jq-based solution would look like (Bash 4.x):
readarray -t values < <(jq -r '.[]' myfile.json)

2 of 4
4

jq is good enough to solve this problem

paste -s <(jq '.files[].name' YourJsonString) <(jq '.files[].age' YourJsonString) <( jq '.files[].websiteurl' YourJsonString) 

So that you get a table and you can grep any rows or awk print any columns you want