[英]Parse a nested variable from YAML file in bash
需要将此链接中的复杂.yaml
文件输入bash脚本,该脚本作为在Amazon Linux 2的EC2实例上运行的自动化程序的一部分运行。请注意,上面链接中的.yaml
文件包含许多对象,并且我需要提取在文件中定义的许多对象之一内部定义的环境变量之一。
具体来说,如何将
CALICO_IPV4POOL_CIDR
变量的192.168.0.0/16
值提取到bash变量中?
- name: CALICO_IPV4POOL_CIDR
value: "192.168.0.0/16"
我已经阅读了很多其他的文章和博客文章,这些文章和文章分析的是更平坦,更简单的.yaml
文件,但是其他示例都没有显示如何在此问题中提取嵌套值,例如CALICO_IPV4POOL_CIDR
的value
。
MYVAR=$(\
curl https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml | \
grep -A 1 CALICO_IPV4POOL_CIDR | \
grep value | \
cut -d ':' -f2 | \
tr -d ' "')
将curl https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml
替换为您要采购的文件。 这将通过管道传递到grep -A 1 CALICO_IPV4POOL_CIDR
。 这将为您提供两行文本:名称行和值行。 这将通过管道传递到grep value
,这现在为我们提供了仅包含该值的行。 然后将其传递给cut -d ':' -f2
,它使用冒号作为分隔符,并为我们提供第二个字段。 $(...)
执行附带的脚本,并将其分配给MYVAR
。 在此脚本之后, echo $MYVAR
应该产生192.168.0.0/16
。
如果您能够安装新的依赖项,并计划处理大量yaml文件,则yq
是jq的包装,可以处理yaml。 这将允许一种安全的(非grep)访问嵌套yaml值的方法。
用法看起来像MY_VALUE=$(yq '.myValue.nested.value' < config-file.yaml)
或者, 如何从Linux Shell脚本解析YAML文件? 有一个仅用于bash的解析器,您可以用来获取价值。
正确的方法是使用脚本语言和YAML解析库来提取您感兴趣的字段。
这是如何在Python中执行此操作的示例。 如果您是真正进行此操作,则可能会将其拆分为多个功能,并具有更好的错误报告功能。 从字面上看,这只是为了说明calico.yaml
格式引起的一些困难,该格式是将多个YAML文档连接在一起,而不仅仅是一个。 您还必须遍历文档内部的一些列表,以便提取您感兴趣的字段。
#!/usr/bin/env python3
import yaml
def foo():
with open('/tmp/calico.yaml', 'r') as fil:
docs = yaml.safe_load_all(fil)
doc = None
for candidate in docs:
if candidate["kind"] == "DaemonSet":
doc = candidate
break
else:
raise ValueError("no YAML document of kind DaemonSet")
l1 = doc["spec"]
l2 = l1["template"]
l3 = l2["spec"]
l4 = l3["containers"]
for containers_item in l4:
l5 = containers_item["env"]
env = l5
for entry in env:
if entry["name"] == "CALICO_IPV4POOL_CIDR":
return entry["value"]
raise ValueError("no CALICO_IPV4POOL_CIDR entry")
print(foo())
但是,有时您现在需要解决方案,而shell脚本非常擅长于此。
如果您遇到的是API端点,那么YAML通常会被漂亮地打印,因此您可以摆脱在任意YAML上不起作用的方式提取文本。
如下所示的内容应该相当健壮:
cat </tmp/calico.yaml | grep -A1 CALICO_IPV4POOL_CIDR | grep value: | cut -d: -f2 | tr -d ' "'
尽管最后需要使用正则表达式检查提取的值确实是有效的IPv4 CIDR表示法。
这里的关键是grep -A1 CALICO_IPV4POOL_CIDR
。
您提到的两元素字典(如下所示)将始终显示为一个块,因为它是YAML文档的子树。
- name: CALICO_IPV4POOL_CIDR
value: "192.168.0.0/16"
总的来说, calico.yaml
中的键不是按字母顺序排序的,但是在{"name": <something>, "value": <something else>}
构造中, name
始终出现在value
之前。
正如其他人所评论的那样,建议使用yq
(以及jq
)(如果可用)。
然后,请尝试以下操作:
value=$(yq -r 'recurse | select(.name? == "CALICO_IPV4POOL_CIDR") | .value' "calico.yaml")
echo "$value"
输出:
192.168.0.0/16
那里有两个问题:
我已经猜到,通过阅读Gregory Nisbett的答案,您需要类型为“ DaemonSet”的YAML文档。
我将尝试仅使用可能已经安装在系统上的工具,因为您提到要在Bash脚本中执行此操作。 我认为您有JQ,因为没有它,在Bash中很难做很多事情!
对于YAML库,我倾向于为此使用Ruby,因为:
建议使用yq ,但是在这种情况下不会有太大帮助,因为您仍然需要可以提取YAML文档的工具。
提取文档后,我将再次使用Ruby将文件另存为JSON。 然后我们可以使用jq。
提取YAML文档
要使用Ruby获取YAML文档并将其另存为JSON,请执行以下操作:
url=...
curl -s $url | \
ruby -ryaml -rjson -e \
"puts YAML.load_stream(ARGF.read)
.select{|doc| doc['kind']=='DaemonSet'}[0].to_json" \
| jq . > calico.json
进一步说明:
我通过jq .
传递了该响应jq .
这样就可以将其格式化为便于阅读,但实际上并不是必须执行此步骤。 我可以在Ruby中做同样的事情,但我猜您希望将Ruby代码保持在最低限度。
选择您想要的钥匙
要选择密钥,可以使用以下JQ查询:
jq -r \
'.spec.template.spec.containers[].env[] | select(.name=="CALICO_IPV4POOL_CIDR") | .value' \
calico.json
进一步说明:
spec.template.spec.containers[].env[]
迭代所有容器及其内部的所有env 放在一起:
#!/usr/bin/env bash
url='https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml'
curl -s $url | \
ruby -ryaml -rjson -e \
"puts YAML.load_stream(ARGF.read)
.select{|doc| doc['kind']=='DaemonSet'}[0].to_json" \
| jq . > calico.json
jq -r \
'.spec.template.spec.containers[].env[] | select(.name=="CALICO_IPV4POOL_CIDR") | .value' \
calico.json
测试:
▶ bash test.sh
192.168.0.0/16
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.