使用 Bash 编辑 YAML 文件

Posted

技术标签:

【中文标题】使用 Bash 编辑 YAML 文件【英文标题】:Edit YAML file with Bash 【发布时间】:2020-12-14 06:57:59 【问题描述】:

我正在尝试编辑以下 YAML 文件

db:
  host: 'x.x.x.x.x'
  main:
    password: 'password_main'
  admin:
    password: 'password_admin'

要编辑 host 部分,我可以使用它

sed -i "/^\([[:space:]]*host: \).*/s//\1'$DNS_ENDPOINT'/" config.yml

但我找不到更新mainadmin 密码的方法(它们是不同的值)。

我试着玩弄\n[[:space:]] 并得到了不同的口味:

sed -i "/^\([[:space:]]*main:\n*[[:space:]]*password: \).*/s//\1'$DNS_ENDPOINT'/" config.yml

但从来没有让它工作。 非常感谢任何帮助!

编辑 - 要求:没有外部二进制文件/工具。只是很好的狂欢。

【问题讨论】:

你应该考虑使用像yq这样的YAML解析器你能安装这样的工具吗? 我宁愿避免这样做。不是要求,但差不多。 所以yq 有两个版本可用。一个使用jq,另一个使用适当的 DSL,用 Go 编写。前者-kislyuk.github.io/yq,后者-github.com/mikefarah/yq。取决于你想使用哪一个 记住 sed != bash,因此无论您采用哪种方法,自定义标志/正则表达式都可能在一个系统上工作而在另一个系统上失败 Just good ol' bash 将是一个糟糕的选择。如果您不能使用yq,那么您应该使用可以从外壳调用的标准工具之一,例如awk。 【参考方案1】:

由于您不想安装yq,您可以使用您很可能已经安装的python。

以下是基本原理:

#!/usr/bin/python

import yaml

with open("file.yml") as f:
    y=yaml.safe_load(f)
    y['db']['admin']['password'] = 'new_admin_pass'
    print(yaml.dump(y, default_flow_style=False, sort_keys=False))

输出:

db:
  host: x.x.x.x.x
  main:
    password: password_main
  admin:
    password: new_admin_pass

类似的python 代码作为单行代码,您可以放入bash 脚本中,看起来像这样(并产生相同的输出):

python -c 'import yaml;f=open("file.yml");y=yaml.safe_load(f);y["db"]["admin"]["password"] = "new_admin_pass"; print(yaml.dump(y, default_flow_style=False, sort_keys=False))'

【讨论】:

@RavinderSingh13 欢迎您!它需要一些编辑来替换硬编码的字符串,但在编辑 YAML 文件时它应该是一个很好的基础。 确实是非常好的答案,为我提供 Python 的健壮性。【参考方案2】:

注意:使用像yq(或yq)这样的YAML解析器将是一种更可靠的解决方案。


但是,我已经使用以下“技术”来更改“预定义”行,尽管 grep 和 sed 的帮助是这样的;

/tmp/config.yml

db:
  host: 'x.x.x.x.x'
  main:
    password: 'password_main'
  admin:
    password: 'password_admin'
    获取“旧密码”所在的行号:
    grep -n 'password_admin' /tmp/config.yml | cut -d ':' -f1
    

    6

    然后,使用sed 用您的新密码覆盖该行:
    sed -i '6s/.*/    password: \'new_admin_pass\'/' /tmp/config.yml
    

新文件现在如下所示:

db:
  host: 'x.x.x.x.x'
  main:
    password: 'password_main'
  admin:
    password: 'new_admin_pass'

注意

请记住,密码中的任何特殊字符(&\/)都会导致 sed 行为不端!

如果缩进发生变化,这可能会失败,因为 YAML 关心缩进。就像我上面提到的,使用 YAML 解析器将是一个更可靠的解决方案!

【讨论】:

谢谢!让我们假设缩进没有改变。其他原因会导致此命令失败吗? @Mornor 我不这么认为,我用这种“风格”来改变 YAML 已经有一段时间了,从来没有失败过。但是,我的 YAML 文件是非常静态的,我只是使用这种技术来更改密码;) 如果新密码包含反向引用元字符,如 &\1 或如果它包含分隔符字符 / 或转义字符 my\new\pass 或其他,它将失败。只要您的密码始终是严格的字母数字,您应该可以,但如果不是,则 YMMV。不过,您不需要 grep+cut+sed - 一个简单的 awk 脚本就可以完成这项工作。并不是说这取决于您知道旧的管理员密码,而您真正想要的只是将管理员(或主)密码更改为新值,无论以前是什么。 ... 我刚刚意识到,如果 main: 密码恰好与 admin: 密码相同,或者该管理员密码恰好也出现在文件。【参考方案3】:
$ awk -v new="'sumthin'" 'prev=="main:"sub(/\047.*/,""); $0=$0 new prev=$1 1' file
db:
  host: 'x.x.x.x.x'
  main:
    password: 'sumthin'
  admin:
    password: 'password_admin'

或者,如果您的新文本可能包含您不想扩展的转义序列(例如 \t\n),这在设置密码时很可能出现,那么:

new="'sumthin'" awk 'prev=="main:"sub(/\047.*/,""); $0=$0 ENVIRON["new"] prev=$1 1' file

请参阅 How do I use shell variables in an awk script?,了解我为什么/如何使用 ENVIRON[] 访问 shell 变量,而不是在第二个脚本中设置 awk 变量。

【讨论】:

【参考方案4】:

没有yq 可靠,但如果您的 yaml 文件结构与问题中显示的相同,则可以使用此awk

pw='new_&pass'
awk -v pw="$pw//&/\\\\&" '/^[[:blank:]]*main:/ 
   print
   if (getline > 0 && $1 == "password:")
      sub(/\047[^\047]*\047/, "\047" pw "\047")
 1' file
db:
  host: 'x.x.x.x.x'
  main:
    password: 'new_&pass'
  admin:
    password: 'password_admin'

【讨论】:

【参考方案5】:

正如专家在其他答案中所提到的那样,yq 应该是正确的方法,但如果有人没有它,那么可以尝试关注。

awk -v s1="'" -v new_pass="new_value_here" '
/main:/
  main_found=1
  print
  next

main_found && /password/
  next

/admin:/ && main_found
  print "    password: " s1 new_pass s1 ORS $0
  main_found=""
  next

1
'  Input_file

注意:如果您想将输出保存到 Input_file 本身,请将 > temp && mv temp Input_file 添加到上述解决方案中。

【讨论】:

以上是关于使用 Bash 编辑 YAML 文件的主要内容,如果未能解决你的问题,请参考以下文章

在 bash 中解析 YAML 文件中的嵌套变量

如何在 OpenCV 中编辑/更新 YAML 文件?

C++ YAML:如何编辑/写入 .yaml 文件中的节点

将小 bash 脚本添加到 cloudbuild.yaml

在 markdown 文件中编辑 YAML Frontmatter

无法使用 SED 快速编辑文件