首页 > 解决方案 > JQ,两个查询,在json的不同部分,合并回来

问题描述

我正在尝试用 jq 提取 kubeconfig 数据。

kubectl config view --raw -o json | jq ...

有一个这样的json产生:

{
    "kind": "Config",
    "apiVersion": "v1",
    "preferences": {},
    "clusters": [
        {
            "name": "some-name",
            "cluster": {
                "server": "https://some-url",
                "certificate-authority-data": "some-cert"
            }
        },
      {
          "name": "another-name",
          "cluster": {
              "server": "https://another-url",
              "certificate-authority-data": "another-cert"
          }
      }
    ],
    "users": [
        {
            "name": "some-name",
            "user": {
                "username": "some-user",
                "password": "some-password"
            }
        },
        {
            "name": "another-name",
            "user": {
                "username": "another-user",
                "password": "another-password"
            }
        }
    ],
    "contexts": [],
    "current-context": "some-context"
}

问题 #1: 对于给定名称“some-name”,我想提取 json:

{
  url: "https://some-url",
  cert: "some-cert",
  username: "some-user",
  password: "some-password"
}

问题 #2: “用户”子部分可以有其他格式

"users": [
    {
        "name": "...",
        "user": {
            "exec": {
                ...
            }

哪里.user.username.user.password两者都可能丢失

在这种情况下,整体查询应该返回“{}”,即使“clusters”查询/分支有结果


问题 3,作为 Jeff Mercado 的后续回答:

我想获取所有集群,加入(分组)名称:

查看手册,https://stedolan.github.io/jq/manual/#Builtinoperatorsandfunctions

“乘法、除法、模数:*、/ 和 %”部分,例如:

jq '{"k": {"a": 1, "b": 2}} * {"k": {"a": 0,"c": 3}}' => {"k": {"a": 0, "b": 2, "c": 3}}'

假设“k”是“name”的值,给出大概正确的结果。因此,按“k”分组,合并 (*) 结果。

我产生了以下查询:

echo "${json}" | jq -r  '(.clusters[] | {(.name): {url: .cluster.server, cert: .cluster["certificate-authority-data"]}}) * (.users[] | {(.name): {user: .user.username, password: .user.password}})'

第一部分返回 {"name": {url: cert}},第二部分是 {"name": {username, password}} 但是,结果不是像 jq 手册中那样合并,而是其他东西......产品?

{
  "some-name": {
    "url": "https://some-url",
    "cert": "some-cert",
    "user": "some-user",
    "password": "some-password"
  }
}
{
  "another-name": {
    "url": "https://another-url",
    "cert": "another-cert"
  },
  "some-name": {
    "user": "some-user",
    "password": "some-password"
  }
}
{
  "some-name": {
    "url": "https://some-url",
    "cert": "some-cert"
  },
  "another-name": {
    "user": "another-user",
    "password": "another-password"
  }
}
{
  "another-name": {
    "url": "https://another-url",
    "cert": "another-cert",
    "user": "another-user",
    "password": "another-password"
  }
}

为什么/它是什么?有点像产品('*')的想法,但不是 jq 教程的想法,因为我(很可能,错误地)理解它


实验: 我现在有 2 个查询产生部分结果。

让我们抓取原始 json(上图)并解析:

read -d '' json << EOF
...
EOF

查询:

echo "${json}" | jq -r '.clusters[] | select(.name=="some-name") | .cluster | {url: .server, cert: .["certificate-authority-data"]}' &&\
echo "${json}" | jq -r '.users[] | select(.name=="some-name") | .user | {user: .username, password: .password}'

将产生拆分输出:

{
  "url": "https://some-url",
  "cert": "some-cert"
}
{
  "user": "some-user",
  "password": "some-password"
}

或者,添加键以进一步合并:

echo "${json}" | jq -r '.clusters[] | select(.name=="some-name") | {name: .name, url: .cluster.server, cert: .cluster["certificate-authority-data"]}' &&\
echo "${json}" | jq -r '.users[] | select(.name=="some-name") | {name: .name, user: .user.username, password: .user.password}'

将产生:


{
  "name": "some-name",
  "url": "https://some-url",
  "cert": "some-cert"
}
{
  "name": "some-name",
  "user": "some-user",
  "password": "some-password"
}

"name" 不是必需的,但可以用作连接操作

标签: jqkubectl

解决方案


因此,您已经知道如何分别按名称获取集群和用户,第一步是在单个过滤器中选择它们:

(.clusters[] | select(.name == $name).cluster), (.users[] | select(.name == $name).user)

这将产生两个单独的对象,集群,然后是用户。但我们想合并它们。有很多方法可以做到这一点。您可以直接添加它们 ( +) 或合并它们 ( *) 但没有真正的区别。您只需在需要的地方将属性重新映射到您想要的名称。

(.clusters[] | select(.name == $name).cluster | {url: .server, cert: ."certificate-authority-data"})
+
(.users[] | select(.name == $name).user | {username, password})

将名称作为参数传递给您的过滤器;

$ kubectl config view --raw -o json | jq --arg name some-name '
(.clusters[] | select(.name == $name).cluster | {url: .server, cert: ."certificate-authority-data"})
+
(.users[] | select(.name == $name).user | {username, password})
'

对于您问题的第二部分,如果事实证明映射的用户缺少关键属性并且您想省略它们,只需select在末尾添加另一个过滤器以测试这些属性并在未找到任何内容时替换为空对象:

... | select(has("username") and has("password")) // {}

jqplay


推荐阅读