【问题标题】:Parse JSON to array in a shell script在 shell 脚本中将 JSON 解析为数组
【发布时间】:2016-07-14 01:45:35
【问题描述】:

我正在尝试将 shell 脚本中的 JSON 对象解析为数组。

例如:[阿曼达,25 岁​​,http://mywebsite.com]

JSON 看起来像:

{
  "name"       : "Amanda", 
  "age"        : "25",
  "websiteurl" : "http://mywebsite.com"
}

我不想使用任何库,最好使用正则表达式或 grep。我已经完成了:

myfile.json | grep name

这给了我“名字”:“阿曼达”。我可以为文件中的每一行循环执行此操作,并将其添加到数组中,但我只需要右侧而不是整行。

【问题讨论】:

  • 为此使用jq
  • 查看[ this ] 问题并向我们展示您为解决此问题所做的一些努力。
  • 这个cat myfile.json | grep name | cut -d ':' -f2 可能会有所帮助。
  • @sjsam:对链接问题的接受答案表明jq 使用良好,但使用了一种错误的方法将其输出读入 shell 数组(至少在撰写本文时 - 已发布评论)。
  • 我假设你的意思不是[Amanda, 25, http://mywebsite.com],而是( "Amanda" 25 "http://mywebsite.com");后者是 bash 的数组语法实际上的样子。 (或者,与declare -p array 一样,也可以按如下方式打印:declare -a array='([0]="Amanda" [1]="25" [2]="http://mywebsite.com")'

标签: json bash shell parsing


【解决方案1】:

如果您确实无法使用正确的 JSON 解析器,例如 jq[1] ,试试awk-based 解决方案

重击 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)

这会将所有属性存储在 Bash 数组 ${values[@]} 中,您可以使用
declare -p values 进行检查。

这些解决方案有局限性:

  • 每个属性都必须在自己的行中,
  • 所有值都必须用双引号括起来,
  • 不支持嵌入的转义双引号。

所有这些限制都强化了使用正确 JSON 解析器的建议。


注意:以下替代解决方案使用 Bash 4.x+ readarray -t values 命令,但它们也适用于 Bash 3.x 替代解决方案 IFS=$'\n' read -d '' -ra values

grep + cut 组合:单个 grep 命令是行不通的(除非你使用 GNU grep - 见下文),但是添加cut 有帮助:

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

GNU grep:使用 -P 支持 PCRE,支持 \K 删除迄今为止匹配的所有内容(比外观更灵活的替代方案-断言背后)以及前瞻断言((?=...)):

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

最后,这是一个纯 Bash (3.x+) 解决方案

在性能方面这是一个可行的替代方案的原因在于,在每次循环迭代中都不会调用外部实用程序;但是,对于较大的输入文件,基于外部实用程序的解决方案会快得多。

#!/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]基于jq 的强大解决方案如下所示(Bash 4.x):
readarray -t values &lt; &lt;(jq -r '.[]' myfile.json)

【讨论】:

    【解决方案2】:

    jq 足以解决这个问题

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

    这样你就可以得到一个表格,你可以 grep 任何行或 awk 打印你想要的任何列

    【讨论】:

      【解决方案3】:

      您可以使用 sed one 衬垫来实现此目的:

      array=( $(sed -n "/{/,/}/{s/[^:]*:[[:blank:]]*//p;}" json ) )
      

      结果:

      $ echo ${array[@]}
      "Amanda" "25" "http://mywebsite.com"
      

      如果您不需要/不想要引号,那么以下 sed 将取消它们:

      array=( $(sed -n '/{/,/}/{s/[^:]*:[^"]*"\([^"]*\).*/\1/p;}' json) )
      

      结果:

      $ echo ${array[@]}
      Amanda 25 http://mywebsite.com
      

      如果您有多个条目,它也可以工作,例如

      $ cat json
      {
        "name"       : "Amanda" 
        "age"        : "25"
        "websiteurl" : "http://mywebsite.com"
      }
      
      {
         "name"       : "samantha"
         "age"        : "31"
         "websiteurl" : "http://anotherwebsite.org"
      }
      
      $ echo ${array[@]}
      Amanda 25 http://mywebsite.com samantha 31 http://anotherwebsite.org
      

      更新:

      正如 cmets 中的 mklement0 所指出的,如果文件包含嵌入的空格,例如 "name" : "Amanda lastname",则可能会出现问题。在这种情况下,Amandalastname 都将被分别读入单独的数组字段中。为避免这种情况,您可以使用readarray,例如,

      readarray -t array < <(sed -n '/{/,/}/{s/[^:]*:[^"]*"\([^"]*\).*/\1/p;}' json2)
      

      这还将解决 cmets 中也提到的任何通配问题。

      【讨论】:

      • 请不要将命令输出解析为带有 array=( $(...) ) 的数组(即使它恰好与示例输入一起使用):它不能按预期使用嵌入的空格,并且可能导致意外通配符。
      • @mklement0 您能否举例说明示例文件的内容在发生意外通配符时的样子?
      • @mklement0 我不确定为什么 globbing 以前不匹配任何内容。可能是因为我在某个时候在测试期间操纵了IFS。然而,在重新启动 shell 之后,globbing 确实发生了。我将更新我的答案以解决此问题。谢谢。
      • 请考虑编辑您的更正以实际与答案一致,而不是在最后作为附录;否则,尝试遵循此答案的人更有可能使用错误代码。
      • (echo ${array[@]} 也是不好的形式——即使array=( "Hello" "Test * Example" "World" ),尽管内容以这种方式正确存储,它也不会将其打印为三个单独的元素。考虑printf '%s\n' "${array[@]}"带引号)。
      猜你喜欢
      • 2017-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-08
      • 1970-01-01
      相关资源
      最近更新 更多