用新值用 yq 替换 Yaml 值作为接受当前值作为输入的 bash 函数的输出

Substitute Yaml values with yq by new value as output of bash function which accept current value as input

概览

详情

我有这个 bash 功能:

function replace_image_repo() {
  echo "nexus.local/"
}

在另一边,我有一个 YAML 文件:

# pod.yaml
kind: Pod
# ...
spec:
  containers:
    - name: web
      image: nginx
    - name: logger
      image: abdennour/logger

我可以用静态值替换 .image 键的所有值:

yq -y '(.. | .image?) |= "mynewimage"' pod.yaml

结果如预期:

# pod.yaml
kind: Pod
# ...
spec:
  containers:
    - name: web
      image: mynewimage # <-- Replacement done successfully
    - name: logger
      image: mynewimage # <-- Replacement done successfully

但是,我想利用 replace_image_repo 上面的 bash 函数并调用它来 根据当前值计算每次出现的新值 :

你可以做得更好。由于 https://github.com/kislyuk/yq 利用了下面 jq 的强大功能,您可以使用后者的 --arg 字段来传递要替换的值。例如您的案例可以定制为传递旧的和新的替换字符串。

此外 jq 过滤器表达式 (.. | .image?) |= "mynewimage" 不是最好的方法,因为 .. 使用 recursive descent parsing,你可能最终得到 null 值在您修改后的结果中。正确的方法是修改过滤器以匹配包含字符串的确切对象并替换为目标值。

建议从 shell 函数中删除非标准关键字 function,尤其是。 bash

replace_image_repo() {
  printf '%s' "nexus.local/"
}

并将yq用作

yq -y --arg old "nginx" \
      --arg new "$(replace_image_repo "nginx")" \
      '.spec.containers |= map( select(.image == $old).image = $new )' yaml

或者如果您的要求是将替换应用于容器下的所有 .image 字段,您可以在不使用 shell 函数的情况下执行以下操作。

yq -y '.spec.containers |= map(.image = "nexus.local/\(.image)")' yaml 

您可以通过将前缀字符串作为参数传递来进一步自定义它

yq -y --arg prefix "nexus.local/" '.spec.containers |= map(.image = ($prefix + "\(.image)") )' yaml 

考虑到您关于必须使用非常复杂的 shell 函数的争论,您可以采用以下方法。两者首先根据 shell 函数(复杂,现在抽象)在 YAML 上解析以获取新的图像名称,然后重新使用结果替换原始文件上的图像名称。

这是因为 jq 还不允许在其表达式上下文中执行任意 shell 函数。

#!/usr/bin/env bash

replace_image_repo() {
  printf '%s' "nexus.local/"
}


convert_to_array() {
    local results=()
    while IFS= read -r image; do
        results+=( \"$(replace_image_repo "$image")\" )
    done < <(yq -r '.spec.containers[].image' yaml)
    local IFS=","
    printf '[%s]' "${results[*]}"
}


yq -y --argjson images "$(convert_to_array)" \
    'reduce range(0; $images | length) as $i (.;
       .spec.containers[$i].image = $images[$i])' yaml