从bash中的YAML文件解析一个嵌套变量

时间:2019-03-01 00:57:49

标签: bash kubernetes yaml

需要将this link中的复杂.yaml文件输入bash脚本,该脚本作为在Amazon Linux 2的EC2实例上运行的自动化程序的一部分运行。请注意,{{1}上面链接中的}文件包含许多对象,我需要提取文件中定义的许多对象之一内部定义的环境变量之一。

  

具体来说,如何将.yaml变量的192.168.0.0/16值提取到bash变量中?

CALICO_IPV4POOL_CIDR

我还阅读了许多其他文章,以及有关解析更平坦,更简单的 - name: CALICO_IPV4POOL_CIDR value: "192.168.0.0/16" 文件的博客条目,但其他示例都没有显示如何提取{{1}的.yaml之类的嵌套值}}。

5 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:2)

如果您能够安装新的依赖项,并计划处理大量Yaml文件,那么yq是jq的包装,可以处理yaml。它将提供一种安全的(非grep)访问嵌套yaml值的方法。

用法看起来像MY_VALUE=$(yq '.myValue.nested.value' < config-file.yaml)

或者,How can I parse a YAML file from a Linux shell script?具有仅用于bash的解析器,您可以使用该解析器来获取值。

答案 2 :(得分:2)

正确的方法是使用脚本语言和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之前。

答案 3 :(得分:1)

在其他人评论时,建议使用yq(以及jq)(如果有)。
然后,请尝试以下操作:

value=$(yq -r 'recurse | select(.name? == "CALICO_IPV4POOL_CIDR") | .value' "calico.yaml")
echo "$value"

输出:

192.168.0.0/16

答案 4 :(得分:0)

您在那里遇到两个问题:

  • 如何从具有多个文档的文件中读取YAML文档
  • 如何从该YAML文档中选择所需的密钥

我猜您通过阅读Gregory Nisbett的答案就需要“ DaemonSet”类型的YAML文档。

我将尝试仅使用可能已经在系统上安装的工具,因为您提到要在Bash脚本中执行此操作。我认为您有JQ,因为没有它,在Bash中很难做很多事情!

对于YAML库,我倾向于为此使用Ruby,因为:

  • 大多数系统都有Ruby
  • Ruby的Psych库自Ruby 1.9起已捆绑在一起
  • 与我的Ruby相比,Python中的PyYAML库有些不灵活,有时会被破坏
  • 默认情况下,通常不安装Perl中的YAML库

建议使用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

进一步的解释:

  • YAML.load_stream读取YAML文档,并将它们全部作为数组返回
  • ARGF.read读取通过STDIN传递的文件
  • 通过Ruby的选择,可以根据类型键轻松选择YAML文档
  • 然后我们将元素4转换为JSON。

我通过jq .传递了该响应,以便将其格式化为便于人类阅读,但实际上并没有必要执行此步骤。我可以在Ruby中做同样的事情,但我猜您希望将Ruby代码保持在最低限度。

选择所需的密钥

要选择密钥,可以使用以下JQ查询:

jq -r \
  '.spec.template.spec.containers[].env[] | select(.name=="CALICO_IPV4POOL_CIDR") | .value' \
  calico.json                                                          

进一步的解释:

  • 第一部分spec.template.spec.containers[].env[]对所有容器及其中的所有环境进行迭代
  • 然后我们选择名称键等于CALICO_IPV4POOL_CIDR的哈希并返回值
  • -r删除字符串两端的引号

将它们放在一起:

#!/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