How to export bash regular and associative arrays to JSON with JC

In jc version 1.25.7 a new typeset or declare command parser was introduced. This new parser can make exporting bash variables, arrays, and associative arrays to JSON quick and easy.

Here’s an example of using the parser to export a regular bash array to JSON:

$ my_array=("apple" "banana" "cherry")
$ typeset -p | jc --typeset | jq '.[] | select(.name=="my_array")'
{
"name": "my_array",
"value": [
"apple",
"banana",
"cherry"
],
"type": "array",
"readonly": false,
"integer": false,
"lowercase": false,
"uppercase": false,
"exported": false
}

As you can see, jc parses the typeset -p output, which includes all variable and array values along with their metadata. We use jq to select only the array value we are interested in. You can further filter this JSON object down to the value field to only grab the values, but the other metadata might be useful in some situations.

The same technique can be used on bash associative arrays:

$ declare -A user_roles=(
["admin"]="alice"
["manager"]="bob"
["guest"]="charlie"
)
$ typeset -p | jc --typeset | jq '.[] | select(.name=="user_roles")'
{
"name": "user_roles",
"value": {
"manager": "bob",
"admin": "alice",
"guest": "charlie"
},
"type": "associative",
"readonly": false,
"integer": false,
"lowercase": false,
"uppercase": false,
"exported": false
}

Here we see the value field is a normal JSON object with key/value pairs.

Of course we can export all of our bash variables to JSON by not filtering the output with jq:

$ typeset -p | jc --typeset -p
[
{
"name": "BASH",
"value": "/opt/homebrew/bin/bash",
"type": "variable",
"readonly": false,
"integer": false,
"lowercase": false,
"uppercase": false,
"exported": false
},
{
"name": "BASHOPTS",
"value": "checkwinsize:cmdhist:complete_fullquote:expand_aliases:extquote:force_fignore:globasciiranges:globskipdots:hostcomplete:interactive_comments:patsub_replacement:progcomp:promptvars:sourcepath",
"type": "variable",
"readonly": true,
"integer": false,
"lowercase": false,
"uppercase": false,
"exported": false
},
{
"name": "BASHPID",
"value": null,
"type": "variable",
"readonly": false,
"integer": true,
"lowercase": false,
"uppercase": false,
"exported": false
},
...

The -p option in jc was used to pretty print the JSON output, but this would not be necessary in a script.

Note that the typeset and declare commands format the output literally how the variable is represented internally. For example, values with line feed characters will have a $ character prepended, so you will need to deal with those. One way would be something like this:

$ typeset -p | grep user_roles
declare -A user_roles=([linebreak]=$'line\nbreak' [manager]="bob" [admin]="alice" [guest]="charlie" )
$ typeset -p | jc --typeset | jq '.[] | select(.name=="user_roles")'
{
"name": "user_roles",
"value": {
"linebreak": "$line\\nbreak",
"manager": "bob",
"admin": "alice",
"guest": "charlie"
},
"type": "associative",
"readonly": false,
"integer": false,
"lowercase": false,
"uppercase": false,
"exported": false
}
$ typeset -p | jc --typeset | jq '.[] | select(.name=="user_roles")' | echo -e $(jq -r .value.linebreak[1:])
line
break

Perhaps a future version of jc can remove the $ character in these situations.

For more information about the new typeset parser, see the documentation.

See also:

Published by kellyjonbrazil

I'm a cybersecurity and cloud computing nerd.

Leave a Reply

Discover more from Brazil's Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading