搜索字段并替换 jq 中的其他字段

搜索字段并替换 jq 中的其他字段

我正在尝试搜索具有特定属性的对象,然后更新另一个属性。鉴于此输入:

[
  {
    "replacements": [
      {
        "newValue": "0",
        "yamlPath": "k8s-helm-templates.deployment.containers.abc.image.tag"
      },
      {
        "newValue": "0",
        "yamlPath": "k8s-helm-templates.deployment.containers.def.image.tag"
      },
      {
        "newValue": "0",
        "yamlPath": "k8s-helm-templates.deployment.containers.ghi.image.tag"
      }
    ],
    "yamlFilePath": "k8s/helm/Dev/us-east-1/values.yaml"
  }
]

并将占位符 ( abc-image-tagfor k8s-helm-templates.deployment.containers.abc.image.tag) 用作:

[
  {"name":"abc-image-tag","value":"123"},
  {"name":"def-image-tag","value":"456"}
]

应该得到的结果是给定值被正确替换并且 0 值被过滤,如下所示:

[
  {
    "replacements": [
      {
        "newValue": "123",
        "yamlPath": "k8s-helm-templates.deployment.containers.abc.image.tag"
      },
      {
        "newValue": "456",
        "yamlPath": "k8s-helm-templates.deployment.containers.def.image.tag"
      }
    ],
    "yamlFilePath": "k8s/helm/Dev/us-east-1/values.yaml"
  }
]

尝试了一些技巧并深入研究了文档,但似乎无法让它发挥作用。这对于 jq 来说是可能的吗?使用 bash 执行其他步骤就可以了

答案1

尝试这个:

jq \
--null-input \
--rawfile replacements replacements.json \
--rawfile placeholders placeholders.json \
'
def placeholder_index: .name | gsub("-"; ".") ;
def replacement_index: .yamlPath | sub("^k8s-helm-templates\\.deployment\\.containers\\."; "") ;
def replace($placeholders):
  [
    JOIN(INDEX(.[]; replacement_index); $placeholders[]; placeholder_index) |
    {newvalue: .[0].value, yamlPath: .[1].yamlPath}
  ]
;

$replacements | fromjson as $replacements |
$placeholders | fromjson as $placeholders |
$replacements | .[0].replacements |= replace($placeholders)
'

我可以通过展示我为实现这一目标而构建的原型来解释它。

我假设每个输入都在一个文件中。

cat > replacements.json <<"EOF"
[
  {
    "replacements": [
      {
        "newValue": "0",
        "yamlPath": "k8s-helm-templates.deployment.containers.abc.image.tag"
      },
      {
        "newValue": "0",
        "yamlPath": "k8s-helm-templates.deployment.containers.def.image.tag"
      },
      {
        "newValue": "0",
        "yamlPath": "k8s-helm-templates.deployment.containers.ghi.image.tag"
      }
    ],
    "yamlFilePath": "k8s/helm/Dev/us-east-1/values.yaml"
  }
]
EOF

cat > placeholders.json <<"EOF"
[
  {"name":"abc-image-tag","value":"123"},
  {"name":"def-image-tag","value":"456"}
]
EOF

第一个技巧是获取每个输入的句柄。如果只有一个输入,您通常会从标准输入中读取它,并.在管道开始时引用它。当您有多个输入时,您可以使用--rawfile将每个 JSON 文本加载到命名变量中。当没有明显的“主”输入时,您可以使用--null-inputjq 不从标准输入读取。

--rawfile读取一个字符串,因此需要使用它fromjson来解析一个JSON对象。您还可以用来--slurpfile读取 JSON 对象数组,但您需要挑选第一个元素,因此这仍然是一个额外的步骤。

jq \
--null-input \
--rawfile replacements replacements.json \
--rawfile placeholders placeholders.json \
'
$replacements | fromjson as $replacements |
$placeholders | fromjson as $placeholders |
$replacements, $placeholders
'

您想要的结果看起来像两个 SQL 数据库表之间的内部联接。我这么说是因为 jq 有“SQL 风格的运算符”,可以实现类似的操作。它们的记录很少,所以我在这里通过反复试验找出了它们。

用于INDEX生成一个对象,该对象将输入流中的每个对象映射到一个键,并用于JOIN对两个索引执行内部联接。

的索引$replacements看起来像这样。这里的关键表达式删除了公共前缀以yamlPath留下可变图像标签。

jq \
--null-input \
--rawfile replacements replacements.json \
--rawfile placeholders placeholders.json \
'
$replacements | fromjson as $replacements |
$placeholders | fromjson as $placeholders |
INDEX(
  $replacements[0].replacements[];
  .yamlPath | sub("^k8s-helm-templates\\.deployment\\.containers\\."; "")
)
'
{
  "abc.image.tag": {
    "newValue": "0",
    "yamlPath": "k8s-helm-templates.deployment.containers.abc.image.tag"
  },
  "def.image.tag": {
    "newValue": "0",
    "yamlPath": "k8s-helm-templates.deployment.containers.def.image.tag"
  },
  "ghi.image.tag": {
    "newValue": "0",
    "yamlPath": "k8s-helm-templates.deployment.containers.ghi.image.tag"
  }
}

的索引$placeholders看起来像这样。键表达式将破折号替换为点,以便变量图像标签看起来像 的标签$replacements

jq \
--null-input \
--rawfile replacements replacements.json \
--rawfile placeholders placeholders.json \
'
$replacements | fromjson as $replacements |
$placeholders | fromjson as $placeholders |
INDEX($placeholders[]; .name | gsub("-"; "."))
'
{
  "abc.image.tag": {
    "name": "abc-image-tag",
    "value": "123"
  },
  "def.image.tag": {
    "name": "def-image-tag",
    "value": "456"
  }
}

您将看到两个索引对象中的一些键匹配。这就是我们如何获得JOIN对这些对象进行内部连接的函数。

实际上,您INDEX只需调用一次,因为JOIN无论如何该函数都会有效地创建第二个索引。

JOIN返回“对”列表的流,其中第一个对象来自流输入,第二个对象来自索引输入。

jq \
--null-input \
--rawfile replacements replacements.json \
--rawfile placeholders placeholders.json \
'
$replacements | fromjson as $replacements |
$placeholders | fromjson as $placeholders |
JOIN(
    INDEX(
        $replacements[0].replacements[];
        .yamlPath | sub("^k8s-helm-templates\\.deployment\\.containers\\."; "")
    );
    $placeholders[];
    .name | gsub("-"; ".")
)
'
[
  {
    "name": "abc-image-tag",
    "value": "123"
  },
  {
    "newValue": "0",
    "yamlPath": "k8s-helm-templates.deployment.containers.abc.image.tag"
  }
]
[
  {
    "name": "def-image-tag",
    "value": "456"
  },
  {
    "newValue": "0",
    "yamlPath": "k8s-helm-templates.deployment.containers.def.image.tag"
  }
]

下一步是创建一个新对象,该对象从每个连接的对象中“选择”正确的属性来创建新对象,然后将输出包装JOIN在数组中。

jq \
--null-input \
--rawfile replacements replacements.json \
--rawfile placeholders placeholders.json \
'
$replacements | fromjson as $replacements |
$placeholders | fromjson as $placeholders |
[
  JOIN(
    INDEX(
      $replacements[0].replacements[];
      .yamlPath | sub("^k8s-helm-templates\\.deployment\\.containers\\."; "")
    );
    $placeholders[];
    .name | gsub("-"; ".")
  ) |
  {newvalue: .[0].value, yamlPath: .[1].yamlPath}
]
'
[
  {
    "newvalue": "123",
    "yamlPath": "k8s-helm-templates.deployment.containers.abc.image.tag"
  },
  {
    "newvalue": "456",
    "yamlPath": "k8s-helm-templates.deployment.containers.def.image.tag"
  }
]

最终的解决方案定义了一些函数来为每个表达式命名。它使用更新赋值运算|=符来复制原始结构$replacements并使用连接结果更新内部列表。

相关内容