JSON数组使用jq来bash变量


19

我有一个像这样的JSON数组:

{
  "SITE_DATA": {
    "URL": "example.com",
    "AUTHOR": "John Doe",
    "CREATED": "10/22/2017"
  }
}

我正在使用jq遍历此数组,因此可以将每个项目的键设置为变量名,并将值设置为其值。

例:

  • URL =“ example.com”
  • AUTHOR =“ John Doe”
  • CREATED =“ 10/22/2017”

到目前为止,我在数组上进行了迭代,但是创建了一个字符串:

constants=$(cat ${1} | jq '.SITE_DATA' | jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]")

哪个输出:

URL=example.com
AUTHOR=John Doe
CREATED=10/22/2017

我正在脚本中进一步使用这些变量:

echo ${URL}

但这回显了当前的空输出。我猜我eval在那里需要一个或一个东西,但似乎无法将手指放在上面。

Answers:


29

您的原始版本将eval无法使用,因为作者名称中包含空格-它将被解释为运行Doe环境变量AUTHOR设置为的命令John。实际上,根本不需要管道jq连接- 内部管道和数据流可以将不同的过滤器连接在一起。

您可以制作一个简单得多的jq程序版本:

jq -r '.SITE_DATA | to_entries | .[] | .key + "=\"" + .value + "\""'

输出:

URL="example.com"
AUTHOR="John Doe"
CREATED="10/22/2017"

不需要a map.[]处理通过管道的其余部分将数组中的每个对象作为一个单独的项,因此将最后一个之后的所有内容|分别应用于每个对象。最后,我们只用一个普通的+连接来组装一个有效的shell赋值字符串,包括值周围的引号。

所有管道在这里都很重要-如果没有它们,您将获得无用的错误消息,其中部分程序在微妙的不同上下文中进行评估。

这个字符串eval可以只要字符`$符,换行符和空不会出现在该数据:

eval "$(jq -r '.SITE_DATA | to_entries | .[] | .key + "=\"" + .value + "\""' < data.json)"
echo "$AUTHOR"

像以前一样,在使用时eval,请务必确保自己信任要获取的数据,因为如果它是恶意的或只是意外格式,则可能会出错。


13

以@Michael Homer的答案为基础,您可以eval通过将数据读取到关联数组中来完全避免潜在的不安全因素。

例如,如果您的JSON数据在名为的文件中file.json

#!/bin/bash

typeset -A myarray

while IFS== read -r key value; do
    myarray["$key"]="$value"
done < <(jq -r '.SITE_DATA | to_entries | .[] | .key + "=" + .value ' file.json)

# show the array definition
typeset -p myarray

# make use of the array variables
echo "URL = '${myarray[URL]}'"
echo "CREATED = '${myarray[CREATED]}'"
echo "AUTHOR = '${myarray[URL]}'"

输出:

$ ./read-into-array.sh 
declare -A myarray=([CREATED]="10/22/2017" [AUTHOR]="John Doe" [URL]="example.com" )
URL = 'example.com'
CREATED = '10/22/2017'
AUTHOR = 'example.com'

1
您也可以单独进行赋值,declare -- “$key=$value”并使用$AUTHOR原来的方法使etc起作用,而无需使用数组。它仍然比eval更安全,尽管更改PATH或更改的可能性仍然比此版本小。
迈克尔·荷马”,

1
是的,该数组很好地将变量隔离到您选择的容器中-不会偶然/恶意破坏重要的环境变量。您可以declare --通过将$ key与允许的变量名列表进行比较来确保版本安全。
cas

1

刚意识到我可以遍历结果并评估每次迭代:

constants=$(cat ${1} | jq '.SITE_DATA' | jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]")

for key in ${constants}; do
  eval ${key}
done

让我做:

echo ${AUTHOR}
# outputs John Doe

0

我真的很喜欢@Michel的建议。有时,您实际上可能只是提取一些变量值以使用BASH在该特定服务器中执行任务。因此,所需变量是已知的。这种使用这种方法的方法是避免或多次调用jq来为每个变量设置值的方法,或者甚至避免将read语句与多个变量一起使用,其中一些变量可以是有效的并且为空,从而导致值移位(这是我的问题)

如果.svID [] .ID =“”(sv将获取slotID值,则我以前的方法将导致值转换错误

-rd '\n' getInfo sv slotID <<< $(jq -r '(.infoCMD // "no info"), (.svID[].ID // "none"), (._id // "eeeeee")' <<< $data)

如果您使用curl下载对象,这是我的方法,可以将一些变量重命名为友好名称,以便从数据数组中提取数据

使用eval和filter可以解决这一问题,只需一行,并将产生具有所需名称的变量

eval "$(jq -r '.[0] | {varNameExactasJson, renamedVar1: .var1toBeRenamed, varfromArray: .array[0].var, varValueFilter: (.array[0].textVar|ascii_downcase)} | to_entries | .[] | .key + "=\"" + (.value | tostring) + "\""' <<< /path/to/file/with/object )"  

在这种情况下,优点是可以在第一步中过滤,重命名和格式化所有所需变量。观察其中有。[0] | 如果源来自使用GET的RESTFULL API服务器,则响应数据为:

[{"varNameExactasJson":"this value", "var1toBeRenamed: 1500, ....}]

如果您的数据不是来自数组,即。是一个像这样的对象:

{"varNameExactasJson":"this value", "var1toBeRenamed: 1500, ....}

只需删除初始索引:

eval "$(jq -r '{varNameExactasJson, renamedVar1: .var1toBeRenamed, varfromArray: .array[0].var, varValueFilter: (.array[0].textVar|ascii_downcase)} | to_entries | .[] | .key + "=\"" + (.value | tostring) + "\""' <<< /path/to/file/with/object )"  

这是一个古老的问题,但是我感到很难分享,因为很难找到

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.