【问题标题】:Use yq to update array value使用 yq 更新数组值
【发布时间】:2022-01-31 20:22:25
【问题描述】:

我有两个正在使用的 yaml 文件。第一个 yaml 文件如下所示:

spring.yml

spring:
  cloud:
    gateway:
      routes:
      - id: someid
        uri: someUri
        predicates:
        - Path=/somePath
        filters:
        - RewritePath=/someOtherPath

我有另一个文件,它只包含路线,看起来像这样:

routes.yml

routes:
  - id: someid
    uri: someOtherUri
    predicates:
    - Path=/somePath
    filters:
    - RewritePath=/someNewPath

我的目标是用第二个文件中的路线值更新第一个文件中的路线。请注意,现实中的第一个文件将有许多路线,但出于演示目的,我仅在此示例中显示第一个。我有以下脚本,当 id 匹配时,它会循环更新路由:

#!/bin/sh
OVERRIDE_ROUTE_IDS=$(yq eval '.routes.[].id' routes.yml)
GENERATED_ROUTE_IDS=$(yq eval '.spring.cloud.gateway.routes.[].id' spring.yml)

SAVEIFS=$IFS   # Save current IFS (Internal Field Separator)
IFS=$'\n'      # Change IFS to newline char
OVERRIDE_ROUTE_IDS=($OVERRIDE_ROUTE_IDS) # split the `OVERRIDE_ROUTE_IDS` string into an array by the same name
GENERATED_ROUTE_IDS=($GENERATED_ROUTE_IDS) # split the `GENERATED_ROUTE_IDS` string into an array by the same name
IFS=$SAVEIFS   # Restore original IFS

for (( i=0; i<${#OVERRIDE_ROUTE_IDS[@]}; i++ ))
do
   if [[ "${GENERATED_ROUTE_IDS[*]}" =~ "${OVERRIDE_ROUTE_IDS[$i]}" ]]
   then
      echo "route ID ${OVERRIDE_ROUTE_IDS[$i]} exists in generated routes"
      for (( j=0; j<${#GENERATED_ROUTE_IDS[@]}; j++ ))
      do
         if [[ "${GENERATED_ROUTE_IDS[$j]}" == "${OVERRIDE_ROUTE_IDS[$i]}" ]]
         then
            echo "index of route ${GENERATED_ROUTE_IDS[$j]} is $j"
            echo "$i"
            ROUTE_TO_USE=$(yq eval ".routes.[$i]" routes.yml)
            $(yq ".spring.cloud.gateway.routes.[$j] = $ROUTE_TO_USE" spring.yml)
         fi
      done
   else
      echo "no match so add to top of routes"
   fi
done

我的假设是这个命令应该用新路由更新 spring.yml 文件,而不是用相同 id 标识的那个:

$(yq ".spring.cloud.gateway.routes.[$j] = $ROUTE_TO_USE" application.yml)

但我收到以下错误

Error: Parsing expression: Lexer error: could not match text starting at 1:37 failing at 1:39 unmatched text: "id"

我对此感到困惑,并且不确定我在这一点上做错了什么。作为参考,我使用的是 yq 版本 4.17.2。

【问题讨论】:

  • 当只有 ids 匹配或只用整个 routes.yaml 覆盖时你想覆盖

标签: shell yaml yq


【解决方案1】:

请注意yq 不会发出数据结构,它会发出字符串。例如,$ROUTE_TO_USE

id: someid
uri: someOtherUri
predicates:
  - Path=/somePath
filters:
  - RewritePath=/someNewPath

这是 YAML 源。将其粘贴到以下yq 命令中会导致语法无效; yq 的表达式语法是 not 文字 YAML。这就是错误试图告诉您的内容。

您要做的是在一个 yq 命令中处理两个输入:

yq ea "select(fi==0).spring.cloud.gateway.routes.[$j] = "`
     `"select(fi==1).routes.[$i] | select(fi==0)" spring.yml routes.yml

eaeval-all 的简写,您需要同时处理多个输入文件。 fifileIndex 的简写,用于选择合适的文件。将结果传送到| select(fi==0) 可确保只写出第一个(修改后的)文件。为了便于阅读,我将长字符串拆分为多行 using backticks

【讨论】:

    【解决方案2】:

    我最终从 yq 的创建者那里得到了解决方案。下面的解决方案是我使用的:

    yq ea '
      (select(fi==0) | .spring.cloud.gateway.routes.[].id) as $currentIds |
      (select(fi==1) | [.routes.[] | select( [$currentIds != .id] | all )] ) as $newRoutes |
      ( $newRoutes + .spring.cloud.gateway.routes + .routes) as $routesToMerge |
    
      (
        (($routesToMerge | .[] | {.id: .}) as $item ireduce ({}; . * $item )) as $uniqueMap
        | ( $uniqueMap  | to_entries | .[]) as $item ireduce([]; . + $item.value)
      ) as $mergedArray
      | select(fi == 0) | .spring.cloud.gateway.routes = $mergedArray  
    
    ' spring.yml routes.yml
    

    这与id 匹配。如果有匹配项,它将使用 routes.yml 中的值。如果没有匹配,则将其添加到路线的顶部。

    【讨论】:

      猜你喜欢
      • 2021-05-14
      • 2020-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-08
      • 1970-01-01
      • 2022-08-04
      相关资源
      最近更新 更多