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

时间:2020-07-29 01:18:51

标签: yaml substitution yq

概述

详细信息

我有这个bash功能:

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

另一方面,我有一个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函数并将其调用为根据当前值计算每次出现的新值

  • 例如,nginx必须替换为$(replace_image_repo nginx)的输出nexus.local/nginx

  • 是否可以匹配当前值?

  • 如果这样,是否可以调用Bash函数“ yq -y'.... $(HERE)'”?

1 个答案:

答案 0 :(得分:1)

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

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

建议从shell函数esp中删除非标准关键字functionbash

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

并使用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函数的争论,您可以执行以下方法。在YAML上的两次解析首先基于shell函数(复杂,现在已抽象)获取新的图像名称,然后再使用结果将原始文件中的图像名称替换回原处。

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

#!/usr/bin/env bash

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


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