使用bash以递归方式合并yaml配置文件

时间:2014-09-02 19:11:39

标签: bash recursion yaml

是否可以使用一些智能管道和编码来递归合并yaml文件? 在PHP中,我创建了一个数组(每个模块可以添加或更新系统中/的配置节点)。

目标是导出shellcript,它将合并所有单独的模块文件夹'将文件配置为大合并文件。它更快,更高效,例如,当我们通过FTP部署新版本时,客户不需要模块化。

它应该像PHP函数一样:array_merge_recursive

文件系统结构如下:

mod/a/config/sys.yml
mod/a/config/another.yml
mod/b/config/sys.yml
mod/b/config/another.yml
mod/c/config/totally-new.yml
sys/config/sys.yml

Config看起来像:

date:
   format:
      date_regular: %d-%m-%Y

一个模块可能会这样做:

date:
   format:
      date_regular: regular dates are boring
      date_special: !!!%d-%m-%Y!!!

到目前为止,我有:

#!/bin/bash
#........
cp -R $dir_project/ $dir_to/
for i in $dir_project/mod/*/
do
    cp -R "${i}/." $dir_to/sys/
done

这当然会破坏循环中所有现有的配置文件..(其余的系统文件都是唯一命名的)

基本上,我需要一个用于命令行的yaml解析器,以及一个像array_merge_recursive一样的替代方法。然后一个yaml作家输出它合并。我担心我必须开始学习Python,因为bash不会在这个上削减它。

6 个答案:

答案 0 :(得分:10)

你可以使用例如perl。下一个oneliner:

perl -MYAML::Merge::Simple=merge_files -MYAML -E 'say Dump merge_files(@ARGV)' f1.yaml f2.yaml

用于下一个输入文件:f1.yaml

date:
  epoch: 2342342343
  format:
    date_regular: "%d-%m-%Y"

f2.yaml

date:
  format:
    date_regular: regular dates are boring
    date_special: "!!!%d-%m-%Y!!!"

打印合并后的结果......

---
date:
  epoch: 2342342343
  format:
    date_regular: regular dates are boring
    date_special: '!!!%d-%m-%Y!!!'

因为@Caleb指出该模块现在只是develeloper,所以这里有一个替代品。它有点长,使用两个(但通常可用)模块:

perl -MYAML=LoadFile,Dump -MHash::Merge::Simple=merge -E 'say Dump(merge(map{LoadFile($_)}@ARGV))' f1.yaml f2.yaml

生成与上面相同的内容。

答案 1 :(得分:3)

我推荐yq -myq是用于yaml的瑞士军刀,与jq(用于JSON)非常相似。

答案 2 :(得分:1)

没有

Bash不支持嵌套数据结构(它的地图只有整数 - >>字符串或字符串 - >字符串),因此无法在内存中表示任意YAML文档。

为此任务使用更强大的语言。

答案 3 :(得分:1)

Bash对此有点延伸(它可以完成,但它很容易出错)。如果您只想从 bash shell调用一些(而不是使用bash函数实际编写合并脚本),那么您有几个选项。

我注意到有一个基于Java的yaml-merge工具,但这并不适合我的想象,所以我一直在寻找。最后,我使用两个工具(yaml2jsonjq

拼凑了一些东西
  

警告:由于JSON的功能是YAML的only a subset,因此对于复杂的YAML结构而言,这不是一个无损过程。它适用于很多简单的键/值/序列场景,但是如果您的输入YAML太过花哨,它会搞砸。在您的数据类型上测试它,看它是否符合您的预期。

  1. 使用yaml2json将您的输入转换为JSON:

    yaml2json input1.yml > input1.json
    yaml2json input2.yml > input2.json
    
  2. 使用jq迭代对象并递归合并它们(有关详细信息,请参阅this question and answers)。按重要性的相反顺序列出文件,因为后面的值会破坏之前的值:

    jq -s 'reduce .[] as $item({}; . + $item)' input1.json input2.json > merged.json
    
  3. 把它带回YAML:

    json2yaml merged.json > merged.yml
    
  4. 如果你想编写脚本,当然通常的bash机制是你的朋友。如果你碰巧像我一样在GNU-Make中,这样的事情就可以解决问题:

    .SECONDEXPANSION:
    merged.yml: input1.yml input2.yml
        json2yaml <(jq -s 'reduce .[] as $$item({}; . + $$item)' $(foreach YAML,$^,<(yaml2json $(YAML)))) > $@
    

答案 4 :(得分:1)

晚了聚会,但是我也为此写了一个工具:

https://github.com/benprofessionaledition/yamlmerge

它几乎与Ondra的JVM工具相同(它们甚至都被称为“ yaml merge”),主要区别在于它是用Go语言编写的,因此可以编译为〜3MB的二进制文件,而没有任何外部依赖性。我们在Gitlab-CI容器中使用它。

答案 5 :(得分:0)

有一个合并YAML文件的工具-merge-yaml。 它支持完整的YAML语法,并能够扩展环境变量引用。

我分叉了它,并将其发布为可执行文件.jar的形式。
https://github.com/OndraZizka/yaml-merge

用法:

./bin/yaml-merge.sh ./*.yml > result.yml

它是用Java编写的,因此您需要安装Java(我认为是8及更高版本)。
(顺便说一句,如果有人想贡献,那就太好了。)


通常,从某种意义上说,该工具并不总是知道您真正想要做什么,合并YAML并不是一件容易的事。您可以采用多种方式合并结构。想想这个例子:

foo:
   bar: bar2
   baz: 
      - baz1
---
foo:
   bar: bar1
   baz: 
      - baz2
   goo: gaz1

几乎没有问题/未知出现:

  • 第二棵foo树是否应替换第一棵树?
  • 第二个bar应该替换第一个baz还是合并到一个数组中?
  • 第二个jq数组应该替换第一个数组还是将其合并?
    • 如果合并,那么如何-应该有重复项,还是该工具应使值保持唯一?应该以某种方式管理订单吗?

等等可能有人反对说可能会有一些默认值,但现实世界中的需求通常需要不同的操作。

用于处理数据结构的其他工具和库通过使用元数据定义方案来解决此问题,例如JAXB或Jackson使用Java批注。
对于此通用工具,这不是一个选择,因此用户将必须通过a)输入数据或b)参数来控制它。 a)不切实际,有时甚至是不可能的,b)乏味并且需要像RewriteEngine on RewriteCond %{REQUEST_URI} !^/abc/ RewriteRule ^/?(.*)$ /abc/$1 [END] 那样的精美语法。

也就是说,Caleb的答案可能是您需要的。虽然,该解决方案将您的数据减少到JSON所能提供的程度,但是您将失去注释,以多种方式表示长字符串,在YAML中使用JSON等,这并不是太用户友好。