使用 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"
我让 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"