我有一个像这样的 JSON 数组:
{
"SITE_DATA": {
"URL": "example.com",
"AUTHOR": "John Doe",
"CREATED": "10/22/2017"
}
}
我希望使用 jq 迭代这个数组,这样我就可以将每个项目的键设置为变量名,并将值设置为它的值。
例子:
- URL="example.com"
- 作者=“约翰·多伊”
- 已创建="2017 年 10 月 22 日"
到目前为止,我对数组进行了迭代,但创建了一个字符串:
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
或一些东西,但似乎无法将我的手指放在它上面。
您的原始版本将
eval
无法使用,因为作者姓名中有空格 - 它会被解释为运行Doe
环境变量AUTHOR
设置为John
. 几乎也不需要通过管道jq
连接自己——内部管道和数据流可以将不同的过滤器连接在一起。所有这些只有在您完全信任输入数据(例如,它是由您控制的工具生成)时才有意义。下面详细说明了几个可能的问题,但我们假设数据本身肯定是您目前期望的格式。
您可以制作一个更简单的 jq 程序版本:
输出:
不需要 a
map
:.[]
处理通过管道的其余部分将数组中的每个对象作为单独的 item处理,因此最后一个之后的所有内容都|
分别应用于每个对象。最后,我们只是用普通的连接组装一个有效的 shell 赋值字符串+
,包括适当的引号和在值周围转义@sh
。这里所有的管道都很重要——没有它们你会得到相当无用的错误消息,程序的某些部分是在微妙不同的上下文中评估的。
如果您完全信任输入数据并具有您想要的效果,则此字符串
eval
可以:与以往一样使用 时
eval
,请注意您要信任所获得的数据,因为如果它是恶意的或只是以意外的格式出现,事情可能会非常错误。特别是,如果键包含 shell 元字符$
或空格,这可能会创建一个正在运行的命令。例如,它还可能PATH
意外覆盖环境变量。如果你不信任数据,要么根本不这样做,要么过滤对象以只包含你首先想要的键:
如果值是一个数组,您也可能会遇到问题,所以
.value | tostring | @sh
会更好 - 但是这个警告列表可能是一个很好的理由,首先不要这样做。也可以建立一个关联数组,而不是引用键和值:
在此之后,
${data[CREATED]}
包含创建日期等,无论键或值的内容是什么。这是最安全的选项,但不会导致可以导出的顶级变量。当一个值是一个数组时,它仍然可能产生 Bash 语法错误,或者如果它是一个对象,它可能会产生一个 jq 错误,但不会执行代码或覆盖任何东西。基于@Michael Homer 的回答,您可以
eval
通过将数据读入关联数组来完全避免潜在的不安全。例如,如果您的 JSON 数据位于名为的文件中
file.json
:输出:
刚刚意识到我可以遍历结果并评估每次迭代:
允许我这样做:
我真的很喜欢@Michel 的建议。有时,您可能真的只是提取一些变量值来使用 Bash 在该特定服务器中执行任务。因此,如果已知所需的变量,使用这种方法可以避免多次调用来
jq
设置每个变量的值,或者甚至使用read
具有多个变量的语句,其中一些变量可以是有效的和空的,从而导致值转移(即我的问题)。我之前的方法会导致值偏移错误——如果 .svID[ ].ID="",
sv
将获得slotID值。如果您使用 curl 下载了对象,这是我将一些变量重命名为友好名称的方法,以从数据数组中提取数据。
使用 eval 和 filters 将用一行解决问题,并生成具有所需名称的变量。
在这种情况下,优势在于它会在第一步中过滤、重命名、格式化所有所需的变量。观察那里有 .[0] | 如果源来自使用 GET 的 RESTful API 服务器,响应数据为:
如果您的数据不是来自数组,即是一个对象,例如:
只需删除初始索引: