我希望提供一个结构化的配置文件,让非技术用户编辑尽可能简单(不幸的是它必须是一个文件),所以我想使用YAML。但是我找不到从Unix shell脚本解析这个问题的方法。
答案 0 :(得分:227)
这是一个仅限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)
答案 1 :(得分:85)
我在python中为shell命令行中的YAML查询需求编写了shyaml
。
概述:
$ 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'
几个关键点:
\0
填充输出可用于实体多行输入操作。 subvalue.maintainer
是一个有效的密钥)。subvalue.things.-1
是subvalue.things
序列的最后一个元素。)shyaml github page或shyaml PyPI page上提供了更多示例和文档。
答案 2 :(得分:40)
我的用例可能与原始帖子提出的内容完全相同或不同,但它绝对相似。
我需要将一些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
找到:
并将其替换为="
,而忽略://
(对于网址)s/$/"/g
将"
追加到每行的末尾s/ *=/=/g
删除=
答案 3 :(得分: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
,其中data
是具有yaml值的散列(或数组)。
作为奖励,它会解析Jekyll's front matter就好了。
ruby -ryaml -e "puts YAML::load(open(ARGV.first).read)['tags']" example.md
答案 4 :(得分:13)
鉴于Python3和PyYAML是现在相当容易满足的依赖项,以下内容可能有所帮助:
yaml() {
python3 -c "import yaml;print(yaml.load(open('$1'))$2)"
}
VALUE=$(yaml ~/my_yaml_file.yaml "['a_key']")
答案 5 :(得分:11)
很难说,因为它取决于您希望解析器从YAML文档中提取的内容。对于简单的情况,您可以使用grep
,cut
,awk
等。对于更复杂的解析,您需要使用完整的解析库,例如Python {{3 }或PyYAML。
答案 6 :(得分:10)
我刚刚写了一个解析器,我称之为 Yay!( Yaml ain&#39; Yamlesque!)解析 Yamlesque ,一个小的YAML的子集。所以,如果您正在为Bash寻找100%兼容的YAML解析器,那么这不是它。但是,要引用OP,如果您希望结构化配置文件尽可能简单,非技术用户可以编辑类似YAML的,这可能会引起人们的兴趣。
它是inspred by the earlier answer但是写关联数组(是,它需要Bash 4.x )而不是基本变量。它的实现方式允许在不事先知道密钥的情况下解析数据,以便可以编写数据驱动的代码。
除了键/值数组元素外,每个数组都有一个包含键名列表的keys
数组,一个包含子数组名称和children
键的parent
数组指其父母。
This是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
Here是一个展示如何使用它的示例:
#!/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
}
}
here是解析器:
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 File Separator字符分隔有效的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" |
两个表达方式相似;它们之所以不同,只是因为第一个选择了引用的值,而第二个选择了未引用的值。
使用File Separator(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
是在当前行的缩进级别之上的缩进级别的键,表示当前行所属的集合。集合的键/值对将存储在一个数组中,其名称定义为prefix
和parent_key
的串联。
对于顶级(缩进级别为零),数据集前缀用作父键,因此它没有前缀(它设置为""
)。所有其他数组都以前缀为前缀。
接下来,将当前密钥插入包含密钥的(awk-internal)数组中。该数组在整个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_parse
的所有输出都可以通过bash eval
或source
内置命令解析为bash命令。
答案 7 :(得分:10)
yq是轻巧且可移植的命令行YAML处理器
该项目的目标是将jq或yaml文件保存起来。
(http://mikefarah.github.io/yq/)
作为示例(直接从documentation窃取),给出了一个sample.yaml文件:
---
bob:
item1:
cats: bananas
item2:
cats: apples
然后
yq r sample.yaml bob.*.cats
将输出
- bananas
- apples
答案 8 :(得分:7)
此处是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 repository。
答案 9 :(得分:5)
另一种选择是将YAML转换为JSON,然后使用jq与JSON表示交互,以从中提取信息或编辑它。
我写了一个包含这个胶水的简单bash脚本 - 请参阅Y2J project on GitHub
答案 10 :(得分:5)
将我的答案从How to convert a json response into yaml in bash中移出,因为这似乎是处理从命令行解析YAML文本的权威文章。
我想添加有关yq
YAML实现的详细信息。由于此YAML解析器有两种实现,两种实现都有名称yq
,因此不查看实现的DSL就很难区分使用哪种。有两种可用的实现方式
jq
的包装,是使用PyYAML库以Python进行YAML解析的Python编写的两者都可以通过标准安装程序包管理器在几乎所有主要发行版上进行安装
两个版本在其他方面都有优点和缺点,但有几个要点值得强调(从其回购说明中采纳)
kislyuk / yq
jq
完全采用了DSL,因此对于熟悉后者的用户来说,解析和操作变得非常简单jq
doesn't preserve comments起,在往返转换期间,注释将丢失。xq
使用xmltodict将XML转换为JSON并将其通过管道传输到jq
,您可以应用相同的DSL对对象执行CRUD操作,然后将输出往返返回XML。-i
标志(类似于sed -i
)支持就地编辑模式mikefarah / yq
-i
标志(类似于sed -i
)支持就地编辑模式-C
标志为输出YAML着色(不适用于JSON输出)和子元素的缩进(默认为2个空格)我对以下两个版本的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
两种实现都需要执行各种操作(一些经常使用的操作)
root_key2
的值coffee
添加属性orange_juice
删除属性food
下的所有项目使用kislyuk / yq
yq -y '.root_key2 |= "this is a new value"' yaml
yq -y '.drink.coffee += { time: "always"}' yaml
yq -y 'del(.drink.orange_juice.colour)' yaml
yq -r '.food|paths(scalars) as $p | [($p|join(".")), (getpath($p)|tojson)] | @tsv' yaml
这很简单。您所需要做的就是使用jq
标志将-y
JSON输出转码回YAML。
使用mikefarah / yq
yq w yaml root_key2 "this is a new value"
yq w yaml drink.coffee.time "always"
yq d yaml drink.orange_juice.colour
yq r yaml --printMode pv "food.**"
.. more待更新
答案 11 :(得分:4)
perl -ne 'chomp; printf qq/%s="%s"\n/, split(/\s*:\s*/,$_,2)' file.yml > file.sh
答案 12 :(得分:2)
现在做事情的快速方法(以前的方法对我不起作用):
sudo wget https://github.com/mikefarah/yq/releases/download/v4.4.1/yq_linux_amd64 -O /usr/bin/yq &&\
sudo chmod +x /usr/bin/yq
示例 asd.yaml:
a_list:
- key1: value1
key2: value2
key3: value3
解析根:
user@vm:~$ yq e '.' asd.yaml
a_list:
- key1: value1
key2: value2
key3: value3
解析key3:
user@vm:~$ yq e '.a_list[0].key3' asd.yaml
value3
答案 13 :(得分:1)
我知道这是非常具体的,但我认为我的回答对某些用户有帮助
如果您的计算机上安装了node
和npm
,则可以使用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解析器一起使用。
答案 14 :(得分:1)
每当您需要一种适用于几乎所有使用Python的操作系统(* nix,OSX,Windows)的“如何使用Shell脚本中的YAML / JSON /兼容数据”解决方案时,请考虑yamlpath,它提供了几个用于读取,写入,搜索和合并YAML,EYAML,JSON和兼容文件的命令行工具。由于几乎每个操作系统都预装了Python或安装起来都很繁琐,因此这使yamlpath具有很高的可移植性。更有趣的是:该项目使用非常强大的命令行友好语法定义了一种直观的路径语言,该语法允许访问一个或更多节点。
对于您的特定问题,以及在使用Python's native package manager或操作系统的程序包管理器安装yamlpath之后(某些操作系统可以通过RPM使用yamlpath)
#!/bin/bash
# Read values directly from YAML (or EYAML, JSON, etc) for use in this shell script:
myShellVar=$(yaml-get --query=any.path.no[matter%how].complex source-file.yaml)
# Use the value any way you need:
echo "Retrieved ${myShellVar}"
# Perhaps change the value and write it back:
myShellVar="New Value"
yaml-set --change=/any/path/no[matter%how]/complex --value="$myShellVar" source-file.yaml
尽管您没有指定数据是简单的Scalar值,所以让我们开始讨论。如果您想要的结果是数组怎么办?更具挑战性的是,如果它是一个哈希数组,而您只想要每个结果的一个属性,该怎么办?进一步假设您的数据实际上分散在多个 YAML文件中,并且您需要在单个查询中获得所有结果。这是一个有趣得多的问题。因此,假设您有以下两个YAML文件:
文件: data1.yaml
---
baubles:
- name: Doohickey
sku: 0-000-1
price: 4.75
weight: 2.7g
- name: Doodad
sku: 0-000-2
price: 10.5
weight: 5g
- name: Oddball
sku: 0-000-3
price: 25.99
weight: 25kg
文件: data2.yaml
---
baubles:
- name: Fob
sku: 0-000-4
price: 0.99
weight: 18mg
- name: Doohickey
price: 10.5
- name: Oddball
sku: 0-000-3
description: This ball is odd
应用从data2.yaml到data1.yaml的所有更改后,如何仅通过外壳脚本报告库存中每个项目的sku
?试试这个:
#!/bin/bash
baubleSKUs=($(yaml-merge --aoh=deep data1.yaml data2.yaml | yaml-get --query=/baubles/sku -))
for sku in "${baubleSKUs[@]}"; do
echo "Found bauble SKU: ${sku}"
done
仅需几行代码,您就能获得所需的一切:
Found bauble SKU: 0-000-1
Found bauble SKU: 0-000-2
Found bauble SKU: 0-000-3
Found bauble SKU: 0-000-4
如您所见,yamlpath将非常复杂的问题转变为琐碎的解决方案。注意,整个查询是作为流处理的;该查询没有更改YAML文件,也没有临时文件。
我意识到这是“还可以解决相同问题的另一种工具”,但是在阅读了此处的其他答案之后,yamlpath似乎比大多数替代产品更便携,更耐用。它还完全了解YAML / JSON /兼容文件,并且不需要将YAML转换为JSON即可执行请求的操作。这样,每当您需要更改源YAML文件中的数据时,原始YAML文件中的注释都会保留下来。像某些替代方案一样,yamlpath也可以跨操作系统移植。更重要的是,yamlpath定义了一种非常强大的查询语言,可以进行非常专业/过滤的数据查询。它甚至可以在单个查询中处理来自文件不同部分的结果。
如果您想一次获取或设置数据中的许多值-包括哈希/数组/地图/列表等复杂数据,yamlpath可以做到。是否需要一个值,但不确切知道它在文档中的位置? yamlpath可以找到它,并为您提供确切的路径。是否需要将多个数据文件合并在一起,包括来自STDIN的文件? yamlpath也这样做。此外,yamlpath可以完全理解YAML锚及其别名,无论是具体值还是参考值,始终提供或准确更改您期望的数据。
免责声明:我编写并维护了yamlpath,它基于ruamel.yaml,而后者又基于PyYAML。因此,yamlpath完全符合标准。
答案 15 :(得分:1)
您可以使用equivalent中的yq,该语言是用golang编写的:
./go-yg -yamlFile /home/user/dev/ansible-firefox/defaults/main.yml -key
firefox_version
返回:
62.0.3
答案 16 :(得分: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
答案 17 :(得分:0)
如果您需要一个值,则可以使用一个工具将YAML文档转换为JSON并提供给jq
,例如yq
。
sample.yaml的内容:
---
bob:
item1:
cats: bananas
item2:
cats: apples
thing:
cats: oranges
示例:
$ yq -r '.bob["thing"]["cats"]' sample.yaml
oranges
答案 18 :(得分:0)
我知道我的回答很具体,但是如果已经安装了 PHP 和 Symfony ,那么使用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
来输出已解析的数组,但是您当然可以做更多的事情...:)
答案 19 :(得分:0)
您还可以考虑使用Grunt(JavaScript任务运行器)。可以很容易地与shell集成。它支持读取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 format的更多选项,请检查YAML site以查找可帮助您解析该格式的可用项目,库和其他资源。
其他工具:
解析,读取和创建JSON
答案 20 :(得分:0)
使用 Python 的 PyYAML 或 YAML::Perl 等库最容易进行复杂的解析。
如果你想把所有的 YAML 值解析成 bash 值,试试这个脚本。这也将处理评论。请参阅下面的示例用法:
# pparse.py
import yaml
import sys
def parse_yaml(yml, name=''):
if isinstance(yml, list):
for data in yml:
parse_yaml(data, name)
elif isinstance(yml, dict):
if (len(yml) == 1) and not isinstance(yml[list(yml.keys())[0]], list):
print(str(name+'_'+list(yml.keys())[0]+'='+str(yml[list(yml.keys())[0]]))[1:])
else:
for key in yml:
parse_yaml(yml[key], name+'_'+key)
if __name__=="__main__":
yml = yaml.safe_load(open(sys.argv[1]))
parse_yaml(yml)
test.yml
- folders:
- temp_folder: datasets/outputs/tmp
- keep_temp_folder: false
- MFA:
- MFA: false
- speaker_count: 1
- G2P:
- G2P: true
- G2P_model: models/MFA/G2P/english_g2p.zip
- input_folder: datasets/outputs/Youtube/ljspeech/wavs
- output_dictionary: datasets/outputs/Youtube/ljspeech/dictionary.dict
- dictionary: datasets/outputs/Youtube/ljspeech/dictionary.dict
- acoustic_model: models/MFA/acoustic/english.zip
- temp_folder: datasets/outputs/tmp
- jobs: 4
- align:
- config: configs/MFA/align.yaml
- dataset: datasets/outputs/Youtube/ljspeech/wavs
- output_folder: datasets/outputs/Youtube/ljspeech-aligned
- TTS:
- output_folder: datasets/outputs/Youtube
- preprocess:
- preprocess: true
- config: configs/TTS_preprocess.yaml # Default Config
- textgrid_folder: datasets/outputs/Youtube/ljspeech-aligned
- output_duration_folder: datasets/outputs/Youtube/durations
- sampling_rate: 44000 # Make sure sampling rate is same here as in preprocess config
需要 YAML 值的脚本:
yaml() {
eval $(python pparse.py "$1")
}
yaml "test.yml"
# What python printed to bash:
folders_temp_folder=datasets/outputs/tmp
folders_keep_temp_folder=False
MFA_MFA=False
MFA_speaker_count=1
MFA_G2P_G2P=True
MFA_G2P_G2P_model=models/MFA/G2P/english_g2p.zip
MFA_G2P_input_folder=datasets/outputs/Youtube/ljspeech/wavs
MFA_G2P_output_dictionary=datasets/outputs/Youtube/ljspeech/dictionary.dict
MFA_dictionary=datasets/outputs/Youtube/ljspeech/dictionary.dict
MFA_acoustic_model=models/MFA/acoustic/english.zip
MFA_temp_folder=datasets/outputs/tmp
MFA_jobs=4
MFA_align_config=configs/MFA/align.yaml
MFA_align_dataset=datasets/outputs/Youtube/ljspeech/wavs
MFA_align_output_folder=datasets/outputs/Youtube/ljspeech-aligned
TTS_output_folder=datasets/outputs/Youtube
TTS_preprocess_preprocess=True
TTS_preprocess_config=configs/TTS_preprocess.yaml
TTS_preprocess_textgrid_folder=datasets/outputs/Youtube/ljspeech-aligned
TTS_preprocess_output_duration_folder=datasets/outputs/Youtube/durations
TTS_preprocess_sampling_rate=44000
使用 bash 访问变量:
echo "$TTS_preprocess_sampling_rate";
>>> 44000