如何将bash数组格式化为JSON数组

28

我有一个Bash数组

X=("hello world" "goodnight moon")
["That I want to turn into a json array"]
["hello world", "goodnight moon"]

有没有一种好方法可以将这个转换成一个字符串的json数组,而不必在子shell中循环遍历键名?

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

这显然行不通

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

51

您可以这样做:

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

输出

[
  "hello world",
  "goodnight moon"
]

2
+1 和 +10 的精神:唯一安全和正确的答案。为了处理嵌入的换行符,可以使用以下命令:for f in "${X[@]}"; do printf '%s' "$f" | jq -R -s .; done | jq -s . - that other guy
2
这将把一个空数组转换为 [ "" ] - Jan Gassen
1
在我的实验中,只有当X未声明为数组时才会发生这种情况。 - thm

19

自 jq 1.6 版本起,您可以执行以下操作:

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

提供:

["hello world","goodnight moon"]

这种方式的好处是完全不需要转义。它可以处理包含换行符、制表符、双引号、反斜杠和其他控制字符的字符串。(好吧,它不能处理NUL字符,但你首先不能在bash数组中有它们。)


1
如果在args后面加上双破折号(--),任何以破折号开头的字符串都不会被jq识别为选项:`X=("-sdf" "ksdfj")` `jq --compact-output --null-input '$ARGS.positional' --args -- "${X[@]}"` - Dave Kok
使用连字符和数组展开符 -- "${X[@]}",结果包含了一个空值,即 ["", "hello world","goodnight moon"]。将其之间的空格去掉,即 --"${X[@]}" 给出了期望的结果。 - Uncle Code Monkey
@UncleCodeMonkey,听起来你的数组里确实有一个额外的空字符串。--是一个单独的参数,告诉jq“不要将此点之后的任何内容视为选项,即使它以连字符开头”。如果你去掉空格,它会将--与你数组中的第一项合并在一起。唯一能够工作的方式是,如果数组中的第一项是一个空字符串或者是一个长jq选项的名称。 - Weeble
@Weeble 感谢您的解释!我想我会保留我的脚本,但知道它为什么有效更好!=) - Uncle Code Monkey
这刚刚为我节省了30分钟的Chat-GPT时间。谢谢! - Lescai Ionel

9

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[@]}"

...产生:

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

如果您计划要经常这样做(就像您不愿使用子shell所示),那么像这样的东西,它不依赖于任何子进程,可能更有优势。


请注意,不依赖子进程的特性取决于echo作为shell内置实现的情况,而对于bash来说,它就是这样的。 - John Bollinger
如果是bash的话,你应该使用原生的[[和]]代替[和]。 - Dennis V
1
你似乎有些混淆了,@DennisV.R.。 [ 是标准实用程序,有时也可以作为shell内置(请参见https://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html)。 [[ ... ]]是bash特定的。 因此,在这个答案中,我选择前者而不是后者,以获得更大的可移植性。 但是,请注意,问题确实通过标题,文本和标签指定了bash - John Bollinger
我想提醒一下,在bash中,[[作为本地部分会更好地提高性能,但是作为外部实用程序的[则更适合兼容性,就像你所说的那样。 - Dennis V
这里可能存在的问题是,如果数组包含控制字符(例如换行符、制表符、换页符),它无法正确转义它们,而JSON不允许未转义的控制字符。 - Weeble

1

您可以使用:

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

1
如果值不包含ASCII控制字符,在有效的JSON字符串中必须转义,您也可以使用 sed
$ X=("hello world" "goodnight moon")
$ printf %s\\n "${X[@]}"|sed 's/["\]/\\&/g;s/.*/"&"/;1s/^/[/;$s/$/]/;$!s/$/,/'
["hello world",
"goodnight moon"]

如果值包含ASCII控制字符,您可以像这样处理:
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

0

随着答案的改进

https://dev59.com/q18d5IYBdhLWcg3wcx5V#26809278

脚本生成几种格式,可以在包含时使用。脚本符合BASH规范,并经过shellcheck检查。

#!/bin/bash
#
#
        X=("hello world" "goodnight moon" 'say "boo"' 'foo\bar')
#
#       set parameter to define purpose: return_format
#               php5    -> for 5.x
#               -> https://dev59.com/TGw05IYBdhLWcg3wuUAL#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[@]}"

将会产生输出:

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"]

你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接