如何从Linux Shell脚本解析YAML文件?


192

我希望提供一个结构化的配置文件,对于非技术用户而言,该文件应尽可能地容易编辑(不幸的是,该文件必须是文件),因此我想使用YAML。但是,我找不到从Unix Shell脚本解析此内容的任何方法。


不直接是您的问题,但是如果您的shell批注特别是关于处理不同节点的远程管理(和
Yaml

9
尝试使用yq在外壳中读取/写入yaml文件。项目页面在这里:mikefarah.github.io/yq您可以使用来安装该工具brewapt或者下载二进制文件。读取值就像yq r some.yaml key.value
vdimitrov '18

@kenorb JSON =阳明/ YAML!
瑞典

我发现密切相关的函数pkuczynski的github,其中(对我而言)最好的是来自jasperes的函数,并保存在他自己的github中
splaisan

Answers:


56

我的用例可能与原始帖子要求的完全不同,但肯定是相似的。

我需要引入一些YAML作为bash变量。YAML永远不会超过一个层次。

YAML看起来像这样:

KEY:                value
ANOTHER_KEY:        another_value
OH_MY_SO_MANY_KEYS: yet_another_value
LAST_KEY:           last_value

输出像一个dis:

KEY="value"
ANOTHER_KEY="another_value"
OH_MY_SO_MANY_KEYS="yet_another_value"
LAST_KEY="last_value"

我通过这一行实现了输出:

sed -e 's/:[^:\/\/]/="/g;s/$/"/g;s/ *=/=/g' file.yaml > file.sh
  • s/:[^:\/\/]/="/g查找:并替换为=",而忽略://(对于URL)
  • s/$/"/g追加"到每一行的末尾
  • s/ *=/=/g 删除所有空格 =

13
不确定您要做什么,但是如果您认为这不适用于所有YAML,那么您是对的。这就是为什么我以一些资格开始的原因。我只是分享了适用于我的用例的东西,因为它比当时的其他任何一个问题都能更好地回答问题。这绝对可以扩展。
柯蒂斯·布莱克威尔

3
对代码注入也有点开放,但是正如您所说的,这是向前迈出的一步
Oriettaxx,2014年

1
我只编写过供本地使用的shell脚本,因此我不必担心。但是,如果您知道如何保护它和/或想详细说明,我将不胜感激。
Curtis Blackwell 2014年

2
一级深度yaml具有多种形式-值可拆分为以下缩进线;可以用多种方式对值进行引用,shell不会解析这些值;一切都可以在一行写有括号:{KEY: 'value', ...}; 可能还有其他。最重要的是,如果您打算将结果评估为shell代码,那将是非常不安全的。
贝尼·切尔尼亚夫斯基-帕斯金

280

这是仅使用bash的解析器,它利用sed和awk解析简单的Yaml文件:

function parse_yaml {
   local prefix=$2
   local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
   sed -ne "s|^\($s\):|\1|" \
        -e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s\$|\1$fs\2$fs\3|p" \
        -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p"  $1 |
   awk -F$fs '{
      indent = length($1)/2;
      vname[indent] = $2;
      for (i in vname) {if (i > indent) {delete vname[i]}}
      if (length($3) > 0) {
         vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
         printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3);
      }
   }'
}

它可以理解以下文件:

## global definitions
global:
  debug: yes
  verbose: no
  debugging:
    detailed: no
    header: "debugging started"

## output
output:
   file: "yes"

使用以下方法解析时:

parse_yaml sample.yml

将输出:

global_debug="yes"
global_verbose="no"
global_debugging_detailed="no"
global_debugging_header="debugging started"
output_file="yes"

它还了解由ruby生成的yaml文件,其中可能包含ruby符号,例如:

---
:global:
  :debug: 'yes'
  :verbose: 'no'
  :debugging:
    :detailed: 'no'
    :header: debugging started
  :output: 'yes'

并输出与上一个示例相同的结果。

脚本中的典型用法是:

eval $(parse_yaml sample.yml)

parse_yaml接受一个前缀参数,以便导入的设置都具有一个公共前缀(这将减少名称空间冲突的风险)。

parse_yaml sample.yml "CONF_"

产量:

CONF_global_debug="yes"
CONF_global_verbose="no"
CONF_global_debugging_detailed="no"
CONF_global_debugging_header="debugging started"
CONF_output_file="yes"

请注意,文件中的先前设置可以由以后的设置引用:

## global definitions
global:
  debug: yes
  verbose: no
  debugging:
    detailed: no
    header: "debugging started"

## output
output:
   debug: $global_debug

另一个不错的用法是先解析一个默认文件,然后解析用户设置,因为后面的设置会覆盖第一个设置,因此该设置将起作用:

eval $(parse_yaml defaults.yml)
eval $(parse_yaml project.yml)

3
酷史蒂芬!如果它也可以将yaml -表示法转换为本地bash数组,那将是惊人的!
quickshiftin

3
如果您在awk脚本中更改printf行,那应该很容易做到。请注意,尽管bash不支持多维关联数组,所以最终会得到一个数组+每个值一个键。嗯,应该将其移至github ...
Stefan Farestam 2014年

5
这要求标准的yml缩进2个空格。如果使用的是4位,那么变量将得到两个下划线作为分隔符,例如global__debug代替global_debug
k0pernikus 2014年

3
vaab大家好-虽然我确定您是正确的,但许多读者想从shell解析真实的YAML文件,但(至少对我而言)尚不清楚结果是什么。通过此脚本,我对问题进行了分析,并定义了一个可以合理映射到标准变量的子集。解决实际的YAML文件解析这一更大的问题当然是毫无根据的。
Stefan Farestam,2014年

3
它仅在屏幕上打印输出。以后您将如何访问这些值?
sattu 2016年

96

我已经用shyamlPython 编写了Shell命令行中的YAML查询需求。

概述:

$ pip install shyaml      ## installation

示例的YAML文件(具有复杂功能):

$ cat <<EOF > test.yaml
name: "MyName !!"
subvalue:
    how-much: 1.1
    things:
        - first
        - second
        - third
    other-things: [a, b, c]
    maintainer: "Valentin Lab"
    description: |
        Multiline description:
        Line 1
        Line 2
EOF

基本查询:

$ cat test.yaml | shyaml get-value subvalue.maintainer
Valentin Lab

对复杂值的更复杂的循环查询:

$ cat test.yaml | shyaml values-0 | \
  while read -r -d $'\0' value; do
      echo "RECEIVED: '$value'"
  done
RECEIVED: '1.1'
RECEIVED: '- first
- second
- third'
RECEIVED: '2'
RECEIVED: 'Valentin Lab'
RECEIVED: 'Multiline description:
Line 1
Line 2'

一些要点:

  • 正确处理了所有YAML类型和语法奇数,如多行,带引号的字符串,内联序列...
  • \0 填充输出可用于可靠的多行输入操作。
  • 简单的点分符号来选择子值(即:subvalue.maintainer是有效键)。
  • 提供对序列的索引访问(即:subvalue.things.-1是序列的最后一个元素subvalue.things。)
  • 一次性访问所有序列/结构元素,以用于bash循环。
  • 您可以将YAML文件的整个子部分输出为... YAML,可以很好地融合以便进一步使用shyaml。

shyaml github页面shyaml PyPI页面上提供了更多示例和文档。


1
这太棒了!如果有一个忽略输出中空白的yaml值的标记,那就太好了。现在,它输出“ null”。我将它与envdir一起使用,以将docker-compose文件输出到envdircat docker-compose.yml | shyaml get-value api.environment | grep -v null | awk -F': ' '{print $2 > ("envdir/" $1)}'
JiminyCricket 2015年

@JiminyCricket请使用github问题页面!我至少会对此保持高兴。;)
vaab 2015年

1
不幸的是,shyaml速度太慢了
nowox

42

yq是轻巧且可移植的命令行YAML处理器

该项目的目的是将yaml文件作为jq或sed。

https://github.com/mikefarah/yq#readme

作为一个示例(直接从文档中被盗),给出了一个sample.yaml文件,其内容为:

---
bob:
  item1:
    cats: bananas
  item2:
    cats: apples

然后

yq r sample.yaml bob.*.cats

将输出

- bananas
- apples

它只是缺少过滤功能
Antonin

Formulae.brew.sh/formula/yq在过去一年中安装了26,679。
dustinevan

1
@Antonin我不确定这是否是您的意思,但它现在似乎具有某些过滤功能:mikefarah.gitbook.io/yq/usage/path-expressions
bmaupin

32

可以将一个小的脚本传递给某些解释器,例如Python。使用Ruby及其YAML库的一种简单方法如下:

$ RUBY_SCRIPT="data = YAML::load(STDIN.read); puts data['a']; puts data['b']"
$ echo -e '---\na: 1234\nb: 4321' | ruby -ryaml -e "$RUBY_SCRIPT"
1234
4321

,其中data是带有yaml值的哈希(或数组)。

作为奖励,它可以很好地解析Jekyll的前端问题

ruby -ryaml -e "puts YAML::load(open(ARGV.first).read)['tags']" example.md

1
可以使用吗?您已将yaml回显给ruby解释器。但是在其余的bash脚本下应如何使用此变量?
Znik 2014年

是的,可以使用。该RUBY_SCRIPT变量是一个ruby脚本,可以将其写入文件(使用运行ruby -ryaml <rubyscript_filename>)。它包含将输入文本转换为某些输出文本并将内部内容存储到data变量中的逻辑。回声会输出yaml文本,但您可以使用cat <yaml_filename>它代替管道内容。
拉斐尔2014年

抱歉,我在上面的示例中看不到。首先,变量RUBY_SCRIPT为ruby解释器保留代码。接下来的echo -e模拟任何Yaml数据,这是通过桩重定向到ruby解释器中的。这将ruby代码作为内联脚本调用,并最终输出到输出示例'a'和'b'变量。那么将变量加载到bash的其余可执行代码在哪里?我只看到一种解决方法。将红宝石输出放入临时文件中,应包含以下行:variable ='value',然后通过'将其加载到bash中。临时文件”。但这是解决方法,而不是解决方法。
Znik

1
@Znik一旦在stdout上获得了某些东西,这些东西是由stdin所提供的东西产生的,其余的都依赖于bash编码器的手(提醒一下,如果您需要将stdout其输入变量中,则不必依赖临时文件!请使用x=$(...)甚至read a b c < <(...))。因此,当您确切地知道要在YAML文件中获取的内容并且知道如何编写红宝石行以访问此数据时,这是一个有效的解决方案。即使很粗糙,这也是恕我直言的完整概念证明。尽管如此,它确实不能为您提供完整的bash抽象。
vaab 2014年

是的。你真卑鄙 骗你吧。使用一个变量很简单。但是很多可战斗的不是。读取变量列表<<(执行到stdout)的技巧非常有用:)
Znik

23

鉴于Python3和PyYAML是当今很容易满足的依赖关系,以下内容可能会有所帮助:

yaml() {
    python3 -c "import yaml;print(yaml.safe_load(open('$1'))$2)"
}

VALUE=$(yaml ~/my_yaml_file.yaml "['a_key']")

我喜欢shyaml,但是在断开连接的系统上,这可以节省生命。也应该与绝大多数python2一起使用,例如RHEL。
rsaw

2
也许使用,yaml.safe_load因为它更安全。pyyaml.org/wiki/PyYAMLDocumentation
Jordan Stewart

12

这是Stefan Farestam的答案的扩展版本:

function parse_yaml {
   local prefix=$2
   local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
   sed -ne "s|,$s\]$s\$|]|" \
        -e ":1;s|^\($s\)\($w\)$s:$s\[$s\(.*\)$s,$s\(.*\)$s\]|\1\2: [\3]\n\1  - \4|;t1" \
        -e "s|^\($s\)\($w\)$s:$s\[$s\(.*\)$s\]|\1\2:\n\1  - \3|;p" $1 | \
   sed -ne "s|,$s}$s\$|}|" \
        -e ":1;s|^\($s\)-$s{$s\(.*\)$s,$s\($w\)$s:$s\(.*\)$s}|\1- {\2}\n\1  \3: \4|;t1" \
        -e    "s|^\($s\)-$s{$s\(.*\)$s}|\1-\n\1  \2|;p" | \
   sed -ne "s|^\($s\):|\1|" \
        -e "s|^\($s\)-$s[\"']\(.*\)[\"']$s\$|\1$fs$fs\2|p" \
        -e "s|^\($s\)-$s\(.*\)$s\$|\1$fs$fs\2|p" \
        -e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s\$|\1$fs\2$fs\3|p" \
        -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" | \
   awk -F$fs '{
      indent = length($1)/2;
      vname[indent] = $2;
      for (i in vname) {if (i > indent) {delete vname[i]; idx[i]=0}}
      if(length($2)== 0){  vname[indent]= ++idx[indent] };
      if (length($3) > 0) {
         vn=""; for (i=0; i<indent; i++) { vn=(vn)(vname[i])("_")}
         printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, vname[indent], $3);
      }
   }'
}

此版本支持-字典和列表的表示法以及简称。以下输入:

global:
  input:
    - "main.c"
    - "main.h"
  flags: [ "-O3", "-fpic" ]
  sample_input:
    -  { property1: value, property2: "value2" }
    -  { property1: "value3", property2: 'value 4' }

产生以下输出:

global_input_1="main.c"
global_input_2="main.h"
global_flags_1="-O3"
global_flags_2="-fpic"
global_sample_input_1_property1="value"
global_sample_input_1_property2="value2"
global_sample_input_2_property1="value3"
global_sample_input_2_property2="value 4"

如您所见,这些-项目会自动编号,以便为每个项目获得不同的变量名。由于bash没有多维数组,因此这是一种解决方法。支持多个级别。要解决@briceburg提到的尾随空格的问题,应将值用单引号或双引号引起来。但是,仍然存在一些局限性:当值包含逗号时,字典和列表的扩展会产生错误的结果。此外,还不支持更复杂的结构,如跨越多行的值(如ssh-keys)。

关于代码的几句话:第一个sed命令将字典的缩写形式扩展{ key: value, ...}为正则,并将其转换为更简单的yaml样式。第二个sed调用对列表的缩写表示法相同,并转换[ entry, ... ]为带有该-表示法的逐项列表。第三个sed调用是处理普通字典的原始调用,现在添加了带有-和缩进的句柄列表。该awk部分为每个缩进级别引入一个索引,并在变量名称为空时(即在处理列表时)增加索引。使用计数器的当前值代替空的vname。当上升一级时,计数器清零。

编辑:我为此创建了一个github存储库


11

很难说,因为这取决于您希望解析器从YAML文档中提取的内容。对于简单的情况下,可能能够使用grepcutawk等对于更复杂的分析,你需要使用一个全面的解析库,如Python的PyYAMLYAML :: Perl的


11

我刚刚写了一个解析器,叫做Yay!Yaml不是Yamlesque!),它解析Yamlesque(YAML的一小部分)。因此,如果您正在寻找针对Bash的100%兼容YAML解析器,那不是吗。但是,引用OP,如果您希望结构化的配置文件对于非技术用户来说像YAML一样容易编辑,则可能会引起您的兴趣。

它是由较早的答案引起的,但是编写了关联数组(是的,它需要Bash 4.x)而不是基本变量。这样做的方式是无需先验密钥就可以解析数据,从而可以编写数据驱动的代码。

除键/值数组元素外,每个数组都有一个keys包含键名列表的children数组,一个包含子数组名的数组以及一个parent引用其父级的键。

是Yamlesque的一个示例:

root_key1: this is value one
root_key2: "this is value two"

drink:
  state: liquid
  coffee:
    best_served: hot
    colour: brown
  orange_juice:
    best_served: cold
    colour: orange

food:
  state: solid
  apple_pie:
    best_served: warm

root_key_3: this is value three

是显示如何使用它的示例:

#!/bin/bash
# An example showing how to use Yay

. /usr/lib/yay

# helper to get array value at key
value() { eval echo \${$1[$2]}; }

# print a data collection
print_collection() {
  for k in $(value $1 keys)
  do
    echo "$2$k = $(value $1 $k)"
  done

  for c in $(value $1 children)
  do
    echo -e "$2$c\n$2{"
    print_collection $c "  $2"
    echo "$2}"
  done
}

yay example
print_collection example

输出:

root_key1 = this is value one
root_key2 = this is value two
root_key_3 = this is value three
example_drink
{
  state = liquid
  example_coffee
  {
    best_served = hot
    colour = brown
  }
  example_orange_juice
  {
    best_served = cold
    colour = orange
  }
}
example_food
{
  state = solid
  example_apple_pie
  {
    best_served = warm
  }
}

这里是解析器:

yay_parse() {

   # find input file
   for f in "$1" "$1.yay" "$1.yml"
   do
     [[ -f "$f" ]] && input="$f" && break
   done
   [[ -z "$input" ]] && exit 1

   # use given dataset prefix or imply from file name
   [[ -n "$2" ]] && local prefix="$2" || {
     local prefix=$(basename "$input"); prefix=${prefix%.*}
   }

   echo "declare -g -A $prefix;"

   local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
   sed -n -e "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \
          -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" "$input" |
   awk -F$fs '{
      indent       = length($1)/2;
      key          = $2;
      value        = $3;

      # No prefix or parent for the top level (indent zero)
      root_prefix  = "'$prefix'_";
      if (indent ==0 ) {
        prefix = "";          parent_key = "'$prefix'";
      } else {
        prefix = root_prefix; parent_key = keys[indent-1];
      }

      keys[indent] = key;

      # remove keys left behind if prior row was indented more than this row
      for (i in keys) {if (i > indent) {delete keys[i]}}

      if (length(value) > 0) {
         # value
         printf("%s%s[%s]=\"%s\";\n", prefix, parent_key , key, value);
         printf("%s%s[keys]+=\" %s\";\n", prefix, parent_key , key);
      } else {
         # collection
         printf("%s%s[children]+=\" %s%s\";\n", prefix, parent_key , root_prefix, key);
         printf("declare -g -A %s%s;\n", root_prefix, key);
         printf("%s%s[parent]=\"%s%s\";\n", root_prefix, key, prefix, parent_key);
      }
   }'
}

# helper to load yay data file
yay() { eval $(yay_parse "$@"); }

链接的源文件中有一些文档,下面是代码功能的简短说明。

yay_parse函数首先查找input文件或以退出状态1退出。然后,它确定prefix显式指定的或从文件名派生的数据集。

它将有效bash命令写入其标准输出,如果执行,则定义代表输入数据文件内容的数组。其中的第一个定义了顶级数组:

echo "declare -g -A $prefix;"

请注意,数组声明是关联(-A),这是Bash版本4的功能。声明也是全局(-g)的,因此它们可以在函数中执行,但可用于全局范围,如yay辅助函数:

yay() { eval $(yay_parse "$@"); }

输入数据最初使用进行处理sed。在使用ASCII 文件分隔符分隔有效Yamlesque字段并删除value字段周围的所有双引号之前,它将删除与Yamlesque格式规范不匹配的行。

 local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
 sed -n -e "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \
        -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" "$input" |

这两个表达式是相似的。它们之所以不同只是因为第一个选择了带引号的值,而第二个选择了不带引号的值。

之所以使用文件分隔符(28 / hex 12 / octal 034),是因为它不可能作为不可打印的字符出现在输入数据中。

结果通过管道传输到awk其中,一次处理一行输入。它使用FS字符将每个字段分配给一个变量:

indent       = length($1)/2;
key          = $2;
value        = $3;

所有行都有一个缩进(可能为零)和一个键,但是它们都不都是值。它计算行的缩进级别,该行将包含前导空格的第一个字段的长度除以2。没有任何缩进的顶级项目的缩进级别为零。

接下来,计算出prefix用于当前项目的内容。这就是添加到键名中以形成数组名的内容。root_prefix顶级数组有一个,定义为数据集名称和下划线:

root_prefix  = "'$prefix'_";
if (indent ==0 ) {
  prefix = "";          parent_key = "'$prefix'";
} else {
  prefix = root_prefix; parent_key = keys[indent-1];
}

_ parent_key是当前行的缩进级别上方的缩进级别的键,代表当前行所属的集合。集合的键/值对将被存储在与定义为串接其名称的数组prefixparent_key

对于最高级别(缩进级别为零),数据集前缀用作父键,因此没有前缀(将其设置为"")。所有其他阵列均以根前缀为前缀。

接下来,将当前密钥插入包含密钥的(内部awk)数组中。该数组在整个awk会话中一直存在,因此包含先前行插入的键。使用键的缩进作为数组索引将键插入到数组中。

keys[indent] = key;

由于此数组包含前几行的键,因此删除缩进级别比当前行的缩进级别高的所有键:

 for (i in keys) {if (i > indent) {delete keys[i]}}

这样就留下了包含从缩进级别0的根到当前行的钥匙串的钥匙数组。它将删除当前一行比当前行缩进更深时保留的陈旧键。

最后一部分输出bash命令:不带值的输入行开始新的缩进级别(以YAML的说法是集合),而带值的输入行将键添加到当前集合。

集合的名称是当前行prefix和的串联parent_key

当键具有值时,将具有该值的键分配给当前集合,如下所示:

printf("%s%s[%s]=\"%s\";\n", prefix, parent_key , key, value);
printf("%s%s[keys]+=\" %s\";\n", prefix, parent_key , key);

第一条语句输出命令以将值分配给以该键命名的关联数组元素,第二条语句输出命令以将键添加到集合的以空格分隔的keys列表中:

<current_collection>[<key>]="<value>";
<current_collection>[keys]+=" <key>";

当键没有值时,将启动一个新集合,如下所示:

printf("%s%s[children]+=\" %s%s\";\n", prefix, parent_key , root_prefix, key);
printf("declare -g -A %s%s;\n", root_prefix, key);

第一条语句输出命令,将新集合添加到当前集合的以空格分隔的children列表中,第二条语句输出命令,为新集合声明一个新的关联数组:

<current_collection>[children]+=" <new_collection>"
declare -g -A <new_collection>;

yay_parsebash evalsource内置命令可以将所有的输出解析为bash 命令。


您是否考虑过将其作为GitHub上的项目?还是已经?
丹尼尔

@daniel,它在GitHub中,但不在自己的仓库中-您可以在这里找到它。请参阅examplesusr/lib目录,这些链接在我对问题的回答中。如果有兴趣,我可以将其分解成自己的回购协议。
starfry

4
恭喜您。最初,我将其重写为纯bash,但后来我无法停止自己,并将其重新实现为基本解析器,并支持不能互相依赖的数组和嵌套结构。在github.com/binaryphile/y2s上
Binary Phile '02

5
perl -ne 'chomp; printf qq/%s="%s"\n/, split(/\s*:\s*/,$_,2)' file.yml > file.sh

仅适用于平面配置。它不适用于结构化Yaml。另外,如何防止使用临时file.sh?
Znik 2014年

5

另一种选择是将YAML转换为JSON,然后使用jq与JSON表示进行交互以从中提取信息或对其进行编辑。

我编写了一个包含此胶水的简单bash脚本-请参阅GitHub上的Y2J项目


2

如果你需要一个值你可以一个工具,你的YAML文件JSON和饲料转换jq,例如yq

sample.yaml的内容:

---
bob:
  item1:
    cats: bananas
  item2:
    cats: apples
  thing:
    cats: oranges

例:

$ yq -r '.bob["thing"]["cats"]' sample.yaml 
oranges

1

我知道这是非常具体的,但是我认为我的回答可能对某些用户有帮助。
如果已安装nodenpm安装在计算机上,则可以使用js-yaml
首次安装:

npm i -g js-yaml
# or locally
npm i js-yaml

然后在您的bash脚本中

#!/bin/bash
js-yaml your-yaml-file.yml

另外,如果您正在使用jq,则可以执行类似的操作

#!/bin/bash
json="$(js-yaml your-yaml-file.yml)"
aproperty="$(jq '.apropery' <<< "$json")"
echo "$aproperty"

因为js-yaml将yaml文件转换为json字符串文字。然后,您可以在unix系统中将字符串与任何json解析器一起使用。


1

如果您拥有python 2和PyYAML,则可以使用我编写的名为parse_yaml.py的解析器。它做的一些更巧妙的事情是让您选择一个前缀(以防您拥有多个具有相似变量的文件),并从yaml文件中选择一个值。

例如,如果您具有以下yaml文件:

staging.yaml:

db:
    type: sqllite
    host: 127.0.0.1
    user: dev
    password: password123

prod.yaml:

db:
    type: postgres
    host: 10.0.50.100
    user: postgres
    password: password123

您可以加载两者而不会发生冲突。

$ eval $(python parse_yaml.py prod.yaml --prefix prod --cap)
$ eval $(python parse_yaml.py staging.yaml --prefix stg --cap)
$ echo $PROD_DB_HOST
10.0.50.100
$ echo $STG_DB_HOST
127.0.0.1

甚至樱桃选择您想要的值。

$ prod_user=$(python parse_yaml.py prod.yaml --get db_user)
$ prod_port=$(python parse_yaml.py prod.yaml --get db_port --default 5432)
$ echo prod_user
postgres
$ echo prod_port
5432

1

您可以使用用golang编写的yq 等效项:

./go-yg -yamlFile /home/user/dev/ansible-firefox/defaults/main.yml -key
firefox_version

返回:

62.0.3

0

您也可以考虑使用Grunt(JavaScript任务运行器)。可以轻松地与外壳集成。它支持读取YAML(grunt.file.readYAML)和JSON(grunt.file.readJSON)文件。

这可以通过在Gruntfile.js(或Gruntfile.coffee)中创建任务来实现,例如:

module.exports = function (grunt) {

    grunt.registerTask('foo', ['load_yml']);

    grunt.registerTask('load_yml', function () {
        var data = grunt.file.readYAML('foo.yml');
        Object.keys(data).forEach(function (g) {
          // ... switch (g) { case 'my_key':
        });
    });

};

然后只需从shell运行grunt foo(检查grunt --help可用任务)即可。

此外,您可以使用从任务()传递来的输入变量来实现exec:foo任务(grunt-exec),foo: { cmd: 'echo bar <%= foo %>' }以便以所需的任何格式打印输出,然后将其通过管道传递到另一个命令中。


还有一个与Grunt类似的工具,称为gulp,带有附加插件gulp-yaml

通过以下方式安装: npm install --save-dev gulp-yaml

用法示例:

var yaml = require('gulp-yaml');

gulp.src('./src/*.yml')
  .pipe(yaml())
  .pipe(gulp.dest('./dist/'))

gulp.src('./src/*.yml')
  .pipe(yaml({ space: 2 }))
  .pipe(gulp.dest('./dist/'))

gulp.src('./src/*.yml')
  .pipe(yaml({ safe: true }))
  .pipe(gulp.dest('./dist/'))

要使用YAML格式的更多选项,请在YAML网站上查看可用的项目,库和其他资源,以帮助您解析该格式。


其他工具:

  • 俊勋

    解析,读取和创建JSON


0

我知道我的回答很具体,但是如果已经安装了PHPSymfony,那么使用Symfony的YAML解析器将非常方便。

例如:

php -r "require '$SYMFONY_ROOT_PATH/vendor/autoload.php'; \
    var_dump(\Symfony\Component\Yaml\Yaml::parse(file_get_contents('$YAML_FILE_PATH')));"

在这里,我只是用来var_dump输出已解析的数组,但是您当然可以做更多... :)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.