繁体   English   中英

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

[英]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_CIDRvalue

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

那里有两个问题:

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

我已经猜到,通过阅读Gregory Nisbett的答案,您需要类型为“ DaemonSet”的YAML文档。

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

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

  • 大多数系统都有Ruby
  • 自Ruby 1.9起,Ruby的Psych库已捆绑在一起
  • 与我经验中的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的select允许根据类型键轻松选择YAML文档
  • 然后,我们将元素4转换为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
  • 然后我们选择名称键等于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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM