'How to format a bash array as a JSON array

I have a bash array

X=("hello world" "goodnight moon")

That I want to turn into a json array

["hello world", "goodnight moon"]

Is there a good way for me to turn this into a json array of strings without looping over the keys in a subshell?

(for x in "${X[@]}"; do; echo $x | sed 's|.*|"&"|'; done) | jq -s '.'

This clearly doesn't work

echo "${X[@]}" | jq -s -R '.'


Solution 1:[1]

You can do this:

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

output

[
  "hello world",
  "goodnight moon"
]

Solution 2:[2]

Since jq 1.6 you can do this:

jq --compact-output --null-input '$ARGS.positional' --args -- "${X[@]}"

giving:

["hello world","goodnight moon"]

This has the benefit that no escaping is required at all. It handles strings containing newlines, tabs, double quotes, backslashes and other control characters. (Well, it doesn't handle NUL characters but you can't have them in a bash array in the first place.)

Solution 3:[3]

This ...

X=("hello world" "goodnight moon" 'say "boo"' 'foo\bar')

json_array() {
  echo -n '['
  while [ $# -gt 0 ]; do
    x=${1//\\/\\\\}
    echo -n \"${x//\"/\\\"}\"
    [ $# -gt 1 ] && echo -n ', '
    shift
  done
  echo ']'
}

json_array "${X[@]}"

... yields:

["hello world", "goodnight moon", "say \"boo\"", "foo\\bar"]

If you are planning to do a lot of this (as your reluctance to use a subshell suggests) then something such as this that does not rely on any subprocess is likely to your advantage.

Solution 4:[4]

You can use:

X=("hello world" "goodnight moon")
sed 's/^/[/; s/,$/]/' <(printf '"%s",' "${X[@]}") | jq -s '.'
[
  [
    "hello world",
    "goodnight moon"
  ]
]

Solution 5:[5]

If the values do not contain ASCII control characters, which have to be escaped in strings in valid JSON, you can also use sed:

$ X=("hello world" "goodnight moon")
$ printf %s\\n "${X[@]}"|sed 's/["\]/\\&/g;s/.*/"&"/;1s/^/[/;$s/$/]/;$!s/$/,/'
["hello world",
"goodnight moon"]

If the values contain ASCII control characters, you can do something like this:

X=($'a\ta' $'a\n\\\"')
for((i=0;i<${#X[@]};i++));do
  [ $i = 0 ]&&printf \[
  printf \"
  e=${X[i]}
  e=${e//\\/\\\\}
  e=${e//\"/\\\"}
  for((j=0;j<${#e};j++));do
    c=${e:j:1}
    if [[ $c = [[:cntrl:]] ]];then
      printf '\\u%04x' "'$c"
    else
      printf %s "$c"
    fi
  done
  printf \"
  if((i<=${#X[@]}-2));then
    printf ,
  else
    printf \]
  fi
done

Solution 6:[6]

As improve of answer

https://stackoverflow.com/a/26809278/16566807

Script produce few formats which could be usefull when include. Script meets BASH spec, checked with shellcheck.

#!/bin/bash
#
#
        X=("hello world" "goodnight moon" 'say "boo"' 'foo\bar')
#
#       set parameter to define purpose: return_format
#               php5    -> for 5.x
#               -> https://stackoverflow.com/questions/7073672/how-to-load-return-array-from-a-php-file/7073686
#               php     -> for 7.x and greater
#               json    -> for $array=@file_get_contents($f); json_decode($array, true);
#               /none/  -> for JS to JSON.Parse(myJSON);
#       function call with array as parameter: return_array "${array[@]}"
        return_array() {
                rf="${return_format}"
                if [[ $rf = "php5" ]]; then
                        q=("<?php return array(" ");")
                elif [[ $rf = "php" ]];then
                        q=("<?php return [" "];")
                elif [[ $rf = "json" ]];then
                        q=("{" "}")
                else
                        q=("[" "]")
                fi
                echo -n "${q[0]}"
                while [[ $# -gt 0 ]]; do
                        x=${1//\\/\\\\}
                        echo -n "\"${x//\"/\\\"}\""
                        [[ $# -gt 1 ]] && echo -n ', '
                        shift
                done
                echo "${q[1]}"
        }

echo "PHP 5.x"
return_format="php5"
return_array "${X[@]}"
echo "PHP 7.x"
return_format="php"
return_array "${X[@]}"
echo "JSON for PHP"
return_format="json"
return_array "${X[@]}"
echo "JSON for JS"
return_format=
return_array "${X[@]}"

will produce output:

PHP 5.x
<?php return array("hello world", "goodnight moon", "say \"boo\"", "foo\\bar");
PHP 7.x
<?php return ["hello world", "goodnight moon", "say \"boo\"", "foo\\bar"];
JSON for PHP
{"hello world", "goodnight moon", "say \"boo\"", "foo\\bar"}
JSON for JS
["hello world", "goodnight moon", "say \"boo\"", "foo\\bar"]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 John Bollinger
Solution 4 anubhava
Solution 5 nisetama
Solution 6