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

Zub*_*air 173 shell yaml

我希望提供一个结构化的配置文件,这对非技术用户来说很容易编辑(不幸的是它必须是一个文件),所以我想使用YAML.但是我找不到从Unix shell脚本解析这个问题的方法.

Ste*_*tam 256

这是一个仅限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);
      }
   }'
}
Run Code Online (Sandbox Code Playgroud)

它理解如下文件:

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

## output
output:
   file: "yes"
Run Code Online (Sandbox Code Playgroud)

哪个,使用时解析:

parse_yaml sample.yml
Run Code Online (Sandbox Code Playgroud)

将输出:

global_debug="yes"
global_verbose="no"
global_debugging_detailed="no"
global_debugging_header="debugging started"
output_file="yes"
Run Code Online (Sandbox Code Playgroud)

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

---
:global:
  :debug: 'yes'
  :verbose: 'no'
  :debugging:
    :detailed: 'no'
    :header: debugging started
  :output: 'yes'
Run Code Online (Sandbox Code Playgroud)

并将输出与上一个示例相同的内容.

脚本中的典型用法是:

eval $(parse_yaml sample.yml)
Run Code Online (Sandbox Code Playgroud)

parse_yaml接受前缀参数,以便导入的设置都具有公共前缀(这将降低命名空间冲突的风险).

parse_yaml sample.yml "CONF_"
Run Code Online (Sandbox Code Playgroud)

收益率:

CONF_global_debug="yes"
CONF_global_verbose="no"
CONF_global_debugging_detailed="no"
CONF_global_debugging_header="debugging started"
CONF_output_file="yes"
Run Code Online (Sandbox Code Playgroud)

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

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

## output
output:
   debug: $global_debug
Run Code Online (Sandbox Code Playgroud)

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

eval $(parse_yaml defaults.yml)
eval $(parse_yaml project.yml)
Run Code Online (Sandbox Code Playgroud)

  • 这需要2个空格的标准yml缩进.如果你使用4个空格,那么变量将获得两个下划线作为分隔符,例如`global__debug`而不是`global_debug`. (4认同)
  • 酷斯蒂芬!如果它可以将yaml`-`表示法转换为原生bash数组,那将是惊人的! (3认同)
  • 如果你在awk脚本中更改printf行,这应该很容易做到.请注意,bash不支持多维关联数组,因此最终会得到一个数组+每个值一个键.嗯,应该把它移到github ...... (3认同)
  • 嗨vaab - 虽然我确定你是正确的,许多读者想从shell解析真正的YAML文件,但不是很清楚(至少对我来说)结果是什么.通过这个脚本,我对这个问题进行了尝试,并定义了一个具有合理映射到标准变量的子集.当然没有解决解决实际YAML文件的更大问题的假装. (3认同)
  • 它只在屏幕上打印输出.您将如何在以后访问这些值? (3认同)

vaa*_*aab 91

我已经shyaml在shell命令行中用python 编写了YAML查询需求.

概述:

$ pip install shyaml      ## installation
Run Code Online (Sandbox Code Playgroud)

示例的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
Run Code Online (Sandbox Code Playgroud)

基本查询:

$ cat test.yaml | shyaml get-value subvalue.maintainer
Valentin Lab
Run Code Online (Sandbox Code Playgroud)

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

$ 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'
Run Code Online (Sandbox Code Playgroud)

几个关键点:

  • 所有YAML类型和语法奇怪都被正确处理,如多行,带引号的字符串,内联序列......
  • \0 填充输出可用于实体多行输入操作.
  • 简单的点分表示法来选择子值(即:subvalue.maintainer是一个有效的键).
  • 通过索引访问提供给序列(即:subvalue.things.-1是序列的最后一个元素subvalue.things.)
  • 一次访问所有序列/结构元素以用于bash循环.
  • 你可以将YAML文件的整个子部分输出为... YAML,它可以很好地与shyaml进行进一步的操作.

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


Cur*_*ell 51

我的用例可能与原始帖子提出的内容完全相同或不同,但它绝对相似.

我需要将一些YAML作为bash变量引入.YAML永远不会超过一个级别.

YAML看起来像这样:

KEY:                value
ANOTHER_KEY:        another_value
OH_MY_SO_MANY_KEYS: yet_another_value
LAST_KEY:           last_value
Run Code Online (Sandbox Code Playgroud)

输出就像一个dis:

KEY="value"
ANOTHER_KEY="another_value"
OH_MY_SO_MANY_KEYS="yet_another_value"
LAST_KEY="last_value"
Run Code Online (Sandbox Code Playgroud)

我用这条线实现了输出:

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

  • 不确定你得到了什么,但如果你的意思是这对所有YAML都不起作用,那你就是对的.这就是为什么我开了几个资格.我只是分享了对我的用例有用的东西,因为它当时比其他任何一个更好地回答了这个问题.这绝对可以扩展. (10认同)
  • 那么yaml也有部分...... (9认同)
  • 对代码注入有点开放,但正如你所说的那样是向前迈进了一步 (3认同)
  • 一层深的 yaml 有多种形式——值可以拆分为以下缩进行;值可以以多种方式引用,shell 不会解析;一切都可以用大括号写在一行上:`{KEY: 'value', ...}`; 可能还有其他人。最重要的是,如果您打算将结果评估为 shell 代码,那将是非常不安全的。 (3认同)
  • 我只编写过在本地使用的 shell 脚本,所以这对我来说并不是一个问题。但是,如果您知道如何保护它和/或想详细说明,我将不胜感激。 (2认同)

Raf*_*ael 31

可以将一个小脚本传递给一些解释器,比如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
Run Code Online (Sandbox Code Playgroud)

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

作为奖励,它会解析Jekyll的前端问题.

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

  • 可以用吗?您已将 yaml by echo 放入 ruby​​ 解释器中。但是在 bash 脚本的其余部分应该如何使用这个变量呢? (2认同)

bma*_*pin 26

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

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

http://mikefarah.github.io/yq/

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

---
bob:
  item1:
    cats: bananas
  item2:
    cats: apples
Run Code Online (Sandbox Code Playgroud)

然后

yq r sample.yaml bob.*.cats
Run Code Online (Sandbox Code Playgroud)

将输出

- bananas
- apples
Run Code Online (Sandbox Code Playgroud)

  • https://formulae.brew.sh/formula/yq 去年的安装量为 26,679。 (2认同)

Tor*_*ger 18

鉴于Python3和PyYAML是现在相当容易满足的依赖项,以下内容可能会有所帮助:

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

VALUE=$(yaml ~/my_yaml_file.yaml "['a_key']")
Run Code Online (Sandbox Code Playgroud)

  • 也许使用“yaml.safe_load”,因为它更安全。https://pyyaml.org/wiki/PyYAMLDocumentation (5认同)
  • 被低估的答案 (2认同)
  • 这很棒。我制作了一个调整版本,将数组打印为每行的一个项目。`python3 -c "import yaml;f=yaml.safe_load(open('$1'))$2;print('\n'.join(str(i) for i in f) if type(f)==list else f);"` (2认同)

dog*_*ane 11

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


Ini*_*ian 11

将我的答案从How to convert a json response into yaml in bash,因为这似乎是处理从命令行解析 YAML 文本的权威帖子。

我想添加有关yqYAML 实现的详细信息。由于此 YAML 解析器有两个实现,都具有名称yq,因此如果不查看实现的 DSL,就很难区分正在使用的是哪个。有两种可用的实现是

  1. kislyuk/yq - 更常被谈论的版本,它是一个包装器jq,使用 Python 编写,使用 PyYAML 库进行 YAML 解析
  2. mikefarah/yq - Go 实现,具有使用 go-yaml v3 解析器的自己的动态 DSL。

两者都可以通过几乎所有主要发行版上的标准安装包管理器进行安装

  1. kislyuk/yq -安装说明
  2. mikefarah/yq -安装说明

两个版本都比另一个版本有一些优点和缺点,但要强调一些有效的要点(从他们的回购说明中采用)

基斯柳克/yq

  1. 由于 DSL 完全采用 from jq,对于熟悉后者的用户来说,解析和操作变得非常简单
  2. 支持保留 YAML 标签和样式的模式,但在转换过程中会丢失注释。由于jq 不保留评论,在往返转换期间,评论会丢失。
  3. 作为包的一部分,内置了XML 支持。 一个可执行文件,xq它使用xmltodict将 XML 转码为 JSON并将其通过管道传输到jq,您可以在其上应用相同的 DSL 对对象执行 CRUD 操作并将输出返回到 XML。
  4. 支持带有-i标志的就地编辑模式(类似于sed -i

迈克法拉/yq

  1. 容易在 DSL 中频繁变化,从 2.x - 3.x 迁移
  2. 对锚点、样式和标签的丰富支持。但偶尔寻找错误
  3. 一个相对简单的Path 表达式语法来导航和匹配 yaml 节点
  4. 支持 YAML->JSON、JSON->YAML 格式和漂亮的 YAML 打印(带注释)
  5. 支持带有-i标志的就地编辑模式(类似于sed -i
  6. 支持使用-C标志(不适用于 JSON 输出)和子元素的缩进(默认为 2 个空格)为输出 YAML 着色
  7. 支持大多数 shell 的 Shell 完成 - Bash、zsh(因为spf13/cobra的强大支持用于生成 CLI 标志)

我对以下两个版本的 YAML(也在其他答案中引用)的看法

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
Run Code Online (Sandbox Code Playgroud)

两种实现都要执行的各种操作(一些经常使用的操作)

  1. 在根级别修改节点值 - 更改值 root_key2
  2. 修改数组内容,添加值 - 将属性添加到 coffee
  3. 修改数组内容,删除值 - 从中​​删除属性 orange_juice
  4. 打印带有路径的键/值对 - 对于下的所有项目 food

使用 kislyuk/yq

  1. yq -y '.root_key2 |= "this is a new value"' yaml
    
    Run Code Online (Sandbox Code Playgroud)
  2. yq -y '.drink.coffee += { time: "always"}' yaml
    
    Run Code Online (Sandbox Code Playgroud)
  3. yq -y 'del(.drink.orange_juice.colour)' yaml
    
    Run Code Online (Sandbox Code Playgroud)
  4. yq -r '.food|paths(scalars) as $p | [($p|join(".")), (getpath($p)|tojson)] | @tsv' yaml
    
    Run Code Online (Sandbox Code Playgroud)

这很简单。您所需要的只是将jqJSON 输出转码回带有该-y标志的YAML 。

使用 mikefarah/yq

  1.  yq w yaml root_key2 "this is a new value"
    
    Run Code Online (Sandbox Code Playgroud)
  2.  yq w yaml drink.coffee.time "always"
    
    Run Code Online (Sandbox Code Playgroud)
  3.  yq d yaml drink.orange_juice.colour
    
    Run Code Online (Sandbox Code Playgroud)
  4.  yq r yaml --printMode pv "food.**"
    
    Run Code Online (Sandbox Code Playgroud)

截至 2020 年 12 月 21 日,yqv4 处于测试阶段,支持强大的路径表达式并支持类似于使用jq. 阅读过渡说明 -从 V3 升级


sta*_*fry 10

我刚写了一个叫做Yay的解析器!(Yaml不是Yamlesque!),它解析YAMLES的一小部分Yamlesque.所以,如果你正在为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
Run Code Online (Sandbox Code Playgroud)

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

#!/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
Run Code Online (Sandbox Code Playgroud)

哪个输出:

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
  }
}
Run Code Online (Sandbox Code Playgroud)

这里是解析器:

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 "$@"); }
Run Code Online (Sandbox Code Playgroud)

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

yay_parse函数首先定位input文件或以退出状态1退出.接下来,它确定数据集prefix,显式指定或从文件名派生.

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

echo "declare -g -A $prefix;"
Run Code Online (Sandbox Code Playgroud)

请注意,数组声明是associative(-A),它是Bash版本4的一个特性.声明也是global(-g),因此它们可以在函数中执行,但可以像yayhelper 一样可用于全局作用域:

yay() { eval $(yay_parse "$@"); }
Run Code Online (Sandbox Code Playgroud)

输入数据最初用sed.处理.在使用ASCII 文件分隔符字符分隔有效的Yamlesque字段并删除值字段周围的任何双引号之前,它会删除与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" |
Run Code Online (Sandbox Code Playgroud)

这两个表达方式相似; 它们之所以不同只是因为第一个选择了引用的值,而第二个选择了未引用的值.

使用文件分隔符(28 /十六进制12 /八进制034),因为作为不可打印的字符,它不太可能在输入数据中.

结果通过管道awk输入其一次输入一行的过程.它使用FS字符将每个字段分配给变量:

indent       = length($1)/2;
key          = $2;
value        = $3;
Run Code Online (Sandbox Code Playgroud)

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

接下来,它prefix确定了当前项目的用途.这是添加到键名称以生成数组名称的内容.有一个root_prefix用于其被定义为所述数据集名称和下划线顶层数组:

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

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

对于顶级(缩进级别为零),数据集前缀用作父键,因此它没有前缀(它设置为"").所有其他数组都以前缀为前缀.

接下来,将当前密钥插入包含密钥的(awk-internal)数组中.该数组在整个awk会话中持续存在,因此包含由前一行插入的键.使用其缩进作为数组索引将键插入到数组中.

keys[indent] = key;
Run Code Online (Sandbox Code Playgroud)

因为此数组包含来自前一行的键,所以删除任何缩进级别大于当前行的缩进级别的键:

 for (i in keys) {if (i > indent) {delete keys[i]}}
Run Code Online (Sandbox Code Playgroud)

这使得包含密钥链的密钥数组从缩进级别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);
Run Code Online (Sandbox Code Playgroud)

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

<current_collection>[<key>]="<value>";
<current_collection>[keys]+=" <key>";
Run Code Online (Sandbox Code Playgroud)

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

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

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

<current_collection>[children]+=" <new_collection>"
declare -g -A <new_collection>;
Run Code Online (Sandbox Code Playgroud)

所有输出yay_parse都可以通过bash evalsource内置命令解析为bash 命令.

  • 感谢你.起初,我把它重写为纯粹的bash,但后来我无法阻止自己重新实现它作为一个基本的解析器,支持数组和嵌套结构,不能踩到彼此的名字.它位于https://github.com/binaryphile/y2s. (3认同)

Mar*_*cht 8

这是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);
      }
   }'
}
Run Code Online (Sandbox Code Playgroud)

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

global:
  input:
    - "main.c"
    - "main.h"
  flags: [ "-O3", "-fpic" ]
  sample_input:
    -  { property1: value, property2: "value2" }
    -  { property1: "value3", property2: 'value 4' }
Run Code Online (Sandbox Code Playgroud)

产生以下输出:

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"
Run Code Online (Sandbox Code Playgroud)

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

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

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


Raj*_*mal 7

我曾经使用 python 将 yaml 转换为 json 并在 jq 中进行处理。

python -c "import yaml; import json; from pathlib import Path; print(json.dumps(yaml.safe_load(Path('file.yml').read_text())))" | jq '.'
Run Code Online (Sandbox Code Playgroud)


小智 5

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


jon*_*our 5

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

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