【问题标题】:how to parse json in bash script as an array? array value should have both key:value format如何将bash脚本中的json解析为数组?数组值应该同时具有 key:value 格式
【发布时间】:2021-09-29 19:29:02
【问题描述】:

我的 json.file 看起来像

{
  "price1" : "120.10",
  "price2" : "110.30",
  "price3" : "244.45"
}

我在我的 bash 脚本中使用了下面的 sed 命令来声明从 json 读取的数组

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

这给了我echo ${array[*]} 值的输出

120.10 110.10 244.45

我正在寻找包含键名的数组值 (key:value)。 我想要的输出应该是key:value 格式

price1:120.10 price2:110.10 price3:244.45

有人可以帮助或指导我吗?

【问题讨论】:

  • 您的输入不是有效的 JSON,您的意思是 {"price1":"120.10","price2":"110.30","price3":"244.45"}
  • 啊是的。我的错你是对的。这就是我的正确格式

标签: arrays json bash sed jq


【解决方案1】:

sed 解析json 可能是个坏主意。但是让我们假设它是一种超级简单且常规的json 格式。您离可行的解决方案不远了:

$ array=($(sed -nr 's/.*"(.*)".*"(.*)".*/\1:\2/p' json.file))
$ echo ${array[*]}
price1:120.10 price2:110.30 price3:244.45

substitute 命令的 p 标志告诉 sed 如果有匹配则打印结果。很高兴知道。如果每条感兴趣的行只有两个带引号的字符串,它应该可以工作。正则表达式 .*"(.*)".*"(.*)".* 匹配任何内容 - .* - 后跟一个双引号,再次是任何内容,另一个双引号......括号 - (.*) - 不要更改匹配的内容。它只是在可用于替换字符串的九个缓冲区之一中记录括号之间的匹配部分 - \1:\2。所以这里,替换字符串对应:

<first recorded match>:<second recorded match>

如果您想更具体地了解匹配行,您可以。例如:

sed -nr 's/^  "(.*)" : "(.*)",?$/\1:\2/p' json.file

还指定有 2 个前导空格,冒号前后各有一个空格,最后一个引号之后和行尾之前有一个可选的逗号。

但也有更简单的:

sed -nr 's/[[:space:]",]//gp' json.file

只删除所有空格、双引号和逗号,只打印匹配的行。你猜怎么着?这是你(显然)想要的。

无论如何,请记住,如果您的 json 文件比您显示的更复杂,sed 绝对不是正确的工具。

【讨论】:

  • 当其他用户搜索 SO 时,最好有明确分开的问题。因此,请提出一个单独的问题,并明确说明您想要什么,并发送Minimal, Reproducible Example
  • 只是想了解 sed 命令的哪一部分只获取密钥?假设我的输出只是来自 json 的键而不是值
  • @gmpy1990 我不明白。您是删除了之前的评论还是我评论了错误的问题?
  • 我已经删除了我之前的问题,因为你提到它是一个单独的(不相关的问题)
  • @gmpy1990 哦,我明白了。好的,让我在我的答案中添加一些关于 sed 正则表达式的解释。
【解决方案2】:

对于 bash 脚本,您可能希望使用关联数组:

declare -A prices
while IFS=$'\t' read -r key value; do
    prices[$key]=$value
done < <(
    jq -r 'to_entries[] | [.key, .value] | @tsv' json.file
)

然后,检查数组

declare -p prices
declare -A prices='([price1]="120.10" [price3]="244.45" [price2]="110.30" )'

或对其进行迭代

for key in "${!prices[@]}"; do
    printf '%s => %s\n' "$key" "${prices[$key]}"
done
price1 => 120.10
price3 => 244.45
price2 => 110.30

【讨论】:

    【解决方案3】:
    json='{"price1":"120.10","price2":"110.30","price3":"244.45"}'
    
    array=($(jq -r 'to_entries[] | ( "\(.key):\(.value)")' <<< "$json"))
    echo ${array[@]}
    printf '%s\n' "${array[@]}"
    declare -p array
    
    echo
    
    array=($(jq -r 'to_entries[] | ( "\(.key):\(.value)") | @sh' <<< "$json"))
    echo ${array[@]}
    printf '%s\n' "${array[@]}"
    declare -p array
    
    

    输出:

    price1:120.10 price2:110.30 price3:244.45
    price1:120.10
    price2:110.30
    price3:244.45
    declare -a array='([0]="price1:120.10" [1]="price2:110.30" [2]="price3:244.45")'
    
    'price1:120.10' 'price2:110.30' 'price3:244.45'
    'price1:120.10'
    'price2:110.30'
    'price3:244.45'
    declare -a array='([0]="'\''price1:120.10'\''" [1]="'\''price2:110.30'\''" [2]="'\''price3:244.45'\''")'
    
    

    【讨论】:

      【解决方案4】:

      您的解决方案只需要提取额外的数据:

      array=( $(sed -n '/{/,/}/{s/"\([^"]*\)"[^:]*:[^"]*"\([^"]*\).*/\1:\2/p;}' <<< "$j") )
      echo ${array[@]}
      price1:120.10 price2:110.30 price3:244.45
      

      看起来更简单的解决方案也可能有效,但我假设您是故意这样编写模式的。

      这里的关键技巧是用转义括号 \(...\) 将模式的一部分括起来,然后在输出部分 \1\2 等中对每个进行编号替换。

      【讨论】:

        猜你喜欢
        • 2018-01-03
        • 2015-01-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-21
        • 2019-06-02
        相关资源
        最近更新 更多