I have a bash array
X=(\"hello world\" \"goodnight moon\")
That I want to turn into a json array
[\"hello world\", \"goodni
You can do this:
X=("hello world" "goodnight moon")
printf '%s\n' "${X[@]}" | jq -R . | jq -s .
[
"hello world",
"goodnight moon"
]
If you're OK with a few extra backslashes, bash's printf "%q"
is useful:
X=("hello world" "goodnight moon" 'say "boo"' 'foo\bar')
json="[$(printf '"%q",' "${X[@]}")"
json="${json%,}]"
echo "$json"
["hello\ world","goodnight\ moon","say\ \"boo\"","foo\\bar"]
Regarding the OK-ness of the backslashes: node.js doesn't have a problem with them:
$ node
> x = ["hello\ world","goodnight\ moon","say\ \"boo\"","foo\\bar"]
[ 'hello world',
'goodnight moon',
'say "boo"',
'foo\\bar' ]
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.
You can use:
X=("hello world" "goodnight moon")
sed 's/^/[/; s/,$/]/' <(printf '"%s",' "${X[@]}") | jq -s '.'
[
[
"hello world",
"goodnight moon"
]
]
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