使用 Shell 格式搜索并替换 YAML 中的字符串值

Search & Replace String Value in YAML with format using Shell

我让 YAML 查找值并将其替换为正确的 YAML 格式(使用 space 和引用)。在 YAML 示例下方,我可以使用以下 Sed 命令替换 jdbcUrl 值。但是,需要帮助如何使用 Sed 为 space 加上前缀并引用该值。 下面 Sed 将查找并替换所需的 jdbcUrl。但是,它不会为 space (YAML 标准)添加前缀并添加值的引号。

用于查找和替换的脚本 URL:

DB_URL='jdbc:mysql://localhost:3306/sd?autoReconnect=true'
sed -i -e 's, MYDATABASE,'$DB_URL',g' input.yaml

示例输入 Yaml:

- name: AP_DB
      description: "datasource"
      jndiConfig:
        name: jdbc/AP_DB
      definition:
        type: RDBMS
        configuration:
          jdbcUrl: MYDATABASE
          username: username
          password: password
          driverClassName: com.mysql.jdbc.Driver

需要输出 Yaml:

- name: AP_DB
      description: "datasource"
      jndiConfig:
        name: jdbc/AP_DB
      definition:
        type: RDBMS
        configuration:
          jdbcUrl: 'jdbc:mysql://localhost:3306/sd?autoReconnect=true'
          username: username
          password: password
          driverClassName: com.mysql.jdbc.Driver

你似乎有一些误解似乎阻碍了你解决这个问题:

  • 您的输入文件是无效的 YAML,替换 MYDATABASE 不会解决这个问题。您不能同时具有 name 映射(以键 description 开头)的标量值。我假设您的文件需要如下所示:

    - name: AP_DB
      description: "datasource"
      jndiConfig:
        name: jdbc/AP_DB
      definition:
        type: RDBMS
        configuration:
          jdbcUrl: MYDATABASE
          username: username
          password: password
          driverClassName: com.mysql.jdbc.Driver
    
  • 在 shell 中为分配给 DB_URL 的值添加引号没有任何区别

  • 您没有使用 shell,而是 sed 进行更改。您的 shell 仅用于调用 sed
  • 您使用 -i 调用 sed,这会覆盖您的 input.yaml,这使得很难查看输出是否正确,并且您需要回滚更改
  • space不是前缀,是白色的space,通常需要跟在YAML的value indicator(:)
  • 后面
  • 您在匹配模式中匹配 space,但在替换模式中没有它,替换模式中也没有任何引号。你可能认为周围有$DB_URL,但当然不是
  • 输出中 URL 周围的引号是多余的

如果您真的想要您指定的输出,有几个选项。首先,您可以更改 YAML 中的相关行以包含引号

      jdbcUrl: 'MYDATABASE'

并稍微更改您的 sed 命令:

sed -e 's,MYDATABASE,'$DB_URL',g' < input.yaml

如果您无法更改 input.yaml,您可以将引号(和 space)添加到 sed 替换中:

sed -e 's, MYDATABASE, "'$DB_URL'",g' < input.yaml

或者不使用单引号,将前缀和后缀连接到 $DB_URL 但使用双引号,这样可以扩展 $DB_URL:

sed -e "s, MYDATABASE, '$DB_URL',g" < input.yaml

一旦您确认这些解决方案中的任何一个都有效,您可以将就地替换选项 -i 重新添加到 sed


sed 不是适合此目的的工具,尤其是因为您似乎不熟悉它和 YAML。使用适当的 YAML 解析器来做这些事情。当简单的模式匹配不再能完成工作时,他们往往会继续工作。解析器的转储机制知道何时插入引号,而不是在不需要时愚蠢地插入它们。解析器还会指示您的输入从一开始就是无效的 YAML。

执行此操作需要更多代码,例如在 Python 中,但至少它只匹配恰好是您的替换字符串的映射值,并且不会尝试对键、序列项、YAML 注释内或 ORIG_MYDATABASE 之类的映射值进行替换,如果这些恰好在文件中。使用 sed 防止这种情况发生可能是一个相当大的挑战。

这样的 Python 程序可能看起来像 subst.py:

import sys
from pathlib import Path
from ruamel.yaml import YAML

val = sys.argv[1]
subst = sys.argv[2]
file_name = Path(sys.argv[3])

def update(d, val, sub):
    if isinstance(d, dict):
        for k in d:
            v = d[k]
            if v == val:
                d[k] = sub
            else:
                update(v, val, sub)
    elif isinstance(d, list):
        for item in d:
            update(item, val, sub)

yaml = YAML()
yaml.preserve_quotes = True  # to preserve superfluous quotes in the input
data = yaml.load(file_name)
update(data, val, subst)
yaml.dump(data, file_name)

并从 shell 调用,就像 sed 必须的那样,通过使用:

python subst.py MYDATABASE $DB_URL input.yaml

当然在 URL 周围的输出中不会有引号,因为它们是多余的并且不在输入文件中,但是 datasource 周围的多余引号会被保留。

这是在您的 yaml 文件中更改“键:新值”的更自动化的方法:

yaml_file="input.yaml"
key="jdbcUrl"
new_value="'jdbc:mysql://localhost:3306/sd?autoReconnect=true'"

sed -r "s/^(\s*${key}\s*:\s*).*/${new_value}/" -i "$yaml_file"