【问题标题】:Search & Replace String Value in YAML with format using Shell使用 Shell 搜索和替换 YAML 中的字符串值
【发布时间】:2019-02-09 15:44:03
【问题描述】:

我有 YAML 查找并用正确的 YAML 格式替换值(带空格和引号)。在 Sample YAML 下面,我可以使用下面的 Sed 命令替换 jdbcUrl 值。但是,需要帮助如何使用 Sed 为值添加空格和引号。 在 Sed 下面会找到并替换一个需要的 jdbcUrl。但是,它不会为空格(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

【问题讨论】:

  • sed 是错误的工具,如果你想保证输出总是有效的 YAML,或者相同输入文档的所有可能的公式都将被解析(YAML 有 很多 不同的方式来表示相同的内容,所以这是实践中的一个主要问题)。将 yq 视为解析和生成 YAML 的 jq 包装器。
  • @CharlesDuffy, Re "guarantee": 一个过度半正式且可能在教学上适得其反的吹毛求疵 -- sed @ 987654322@,因此必须有可能以某种方式编写sed 代码来保证所需的输出,大概是通过使用条件分支来超越那些使其不适合解析 YAML 的limitations of regex。 (除此之外,sed 对于这项工作来说是一个糟糕的工具。)

标签: sed yaml substitution


【解决方案1】:

您似乎有一些误解会阻碍您解决这个问题:

  • 您的输入文件是无效的 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,这使得很难查看输出是否正确,您需要回滚您的更改
  • 空格不是前缀,它是通常需要跟在 YAML 的值指示符后面的空格 (:)
  • 您在匹配模式中匹配该空格,但您的替换模式中没有它,替换模式中也没有任何引号。您可能认为$DB_URL 周围有,但当然不是。
  • 输出中 URL 周围的引号是多余的

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

      jdbcUrl: 'MYDATABASE'

并稍微更改您的 sed 命令:

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

如果您无法更改 input.yaml,您只需将引号(和空格)添加到 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 cmets 或 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 周围的多余引号会被保留。

【讨论】:

    【解决方案2】:

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

    yaml_file="input.yaml"
    key="jdbcUrl"
    new_value="'jdbc:mysql://localhost:3306/sd?autoReconnect=true'"
    
    sed -r "s/^(\s*${key}\s*:\s*).*/\1${new_value}/" -i "$yaml_file"
    

    【讨论】:

    • 无痛解决方案!
    • 看在上帝的份上:在 macos 上使用 gnu-sed
    猜你喜欢
    • 1970-01-01
    • 2016-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-23
    • 1970-01-01
    相关资源
    最近更新 更多