如何将 yaml 文件读入 bash 关联数组?
How to read yaml file into bash associative array?
我想把一个yaml文件的内容读入bash关联数组,这是一个简单的键值映射。
示例map.yaml
---
a: "2"
b: "3"
api_key: "somekey:thatcancontainany@chara$$ter"
密钥可以包含除 space
之外的任何字符
该值可以包含不受限制的任何字符 $!:=@etc
永远不变的是键和值之间的分隔符是:
剧本proceses.sh
#!/usr/bin/env bash
declare -A map
# how to read here into map variable, from map.yml file
#map=populatesomehowfrommap.yaml
for key in "${!map[@]}"
do
echo "key : $key"
echo "value: ${map[$key]}"
done
我尝试使用 yq tool,类似于 json 工具 jq,但还没有成功。
一种方法是让 yq 在一行中输出每个 key/value 对,语法如下:
key@value
然后我们可以使用bash's IFS来拆分这些值。
@
只是一个例子,可以用任何单个字符替换
这可行,但请注意以下限制:
- 它不期望嵌套值,只有一个平面列表`
- 字段分隔符(示例中的
@
)在 YAML key/value 的 中不存在
#!/bin/bash
declare -A arr
while IFS="@" read -r key value
do
arr[$key]="$value"
done < <(yq e 'to_entries | .[] | (.key + "@" + .value)' input.yaml)
for key in "${!arr[@]}"
do
echo "key : $key"
echo "value: ${arr[$key]}"
done
$ cat input.yaml
---
a: "bar"
b: "foo"
$
$
$ ./script.sh
key : a
value: bar
key : b
value: foo
$
具有以下限制:
- 单行的简单 YAML
key: "value"
- 密钥可以不能包含
:
- 值总是包含在
"
中
#!/usr/bin/env bash
declare -A map
regex='^([^:]+):[[:space:]]+"(.*)"[[:space:]]*$'
while IFS='' read -r line
do
if [[ $line =~ $regex ]]
then
printf -v map["${BASH_REMATCH[1]}"] '%b' "${BASH_REMATCH[2]}"
else
echo "skipping: $line" 1>&2
fi
done < map.yaml
更新
这是一个使用 yq
的可靠解决方案,如果内置 @tsv
过滤器实施无损 TSV 转义规则而不是 CSV 规则,该解决方案会更简单。
#!/usr/bin/env bash
declare -A map
while IFS=$'\t' read key value
do
printf -v map["$key"] '%b' "$value"
done < <(
yq e '
to_entries | .[] |
[
(.key | sub("\","\") | sub("\n","\n") | sub("\r","\r") | sub("\t","\t")),
(.value | sub("\","\") | sub("\n","\n") | sub("\r","\r") | sub("\t","\t"))
] |
join(" ")
' map.yaml
)
注意: join
需要文字 Tab
我使用了@Fravadona 的答案所以将其标记为答案
对我的用例进行一些修改后,对我有用的看起来像:
DEFS_PATH="definitions"
declare -A ssmMap
for file in ${DEFS_PATH}/*
do
filename=$(basename -- "$file")
projectName="${filename%.*}"
regex='^([^:]+):[[:space:]]*"(.*)"[[:space:]]*$'
while IFS='' read -r line
do
if [[ $line =~ $regex ]]
then
value="${BASH_REMATCH[2]}"
value=${value//"{{ ssm_env }}"/$INFRA_ENV}
value=${value//"{{ ssm_reg }}"/$SSM_REGION}
value=${value//"{{ projectName }}"/$projectName}
printf -v ssmMap["${BASH_REMATCH[1]}"] '%b' "$value"
else
echo "skipping: $line" 1>&2
fi
done < "$file"
done
基本上在实际用例中,我有一个文件夹,其中包含 yaml 定义。我遍历所有这些以形成关联数组 ssmMap
我想把一个yaml文件的内容读入bash关联数组,这是一个简单的键值映射。
示例map.yaml
---
a: "2"
b: "3"
api_key: "somekey:thatcancontainany@chara$$ter"
密钥可以包含除 space
之外的任何字符
该值可以包含不受限制的任何字符 $!:=@etc
永远不变的是键和值之间的分隔符是:
剧本proceses.sh
#!/usr/bin/env bash
declare -A map
# how to read here into map variable, from map.yml file
#map=populatesomehowfrommap.yaml
for key in "${!map[@]}"
do
echo "key : $key"
echo "value: ${map[$key]}"
done
我尝试使用 yq tool,类似于 json 工具 jq,但还没有成功。
一种方法是让 yq 在一行中输出每个 key/value 对,语法如下:
key@value
然后我们可以使用bash's IFS来拆分这些值。
@
只是一个例子,可以用任何单个字符替换
这可行,但请注意以下限制:
- 它不期望嵌套值,只有一个平面列表`
- 字段分隔符(示例中的
@
)在 YAML key/value 的 中不存在
#!/bin/bash
declare -A arr
while IFS="@" read -r key value
do
arr[$key]="$value"
done < <(yq e 'to_entries | .[] | (.key + "@" + .value)' input.yaml)
for key in "${!arr[@]}"
do
echo "key : $key"
echo "value: ${arr[$key]}"
done
$ cat input.yaml
---
a: "bar"
b: "foo"
$
$
$ ./script.sh
key : a
value: bar
key : b
value: foo
$
具有以下限制:
- 单行的简单 YAML
key: "value"
- 密钥可以不能包含
:
- 值总是包含在
"
中
#!/usr/bin/env bash
declare -A map
regex='^([^:]+):[[:space:]]+"(.*)"[[:space:]]*$'
while IFS='' read -r line
do
if [[ $line =~ $regex ]]
then
printf -v map["${BASH_REMATCH[1]}"] '%b' "${BASH_REMATCH[2]}"
else
echo "skipping: $line" 1>&2
fi
done < map.yaml
更新
这是一个使用 yq
的可靠解决方案,如果内置 @tsv
过滤器实施无损 TSV 转义规则而不是 CSV 规则,该解决方案会更简单。
#!/usr/bin/env bash
declare -A map
while IFS=$'\t' read key value
do
printf -v map["$key"] '%b' "$value"
done < <(
yq e '
to_entries | .[] |
[
(.key | sub("\","\") | sub("\n","\n") | sub("\r","\r") | sub("\t","\t")),
(.value | sub("\","\") | sub("\n","\n") | sub("\r","\r") | sub("\t","\t"))
] |
join(" ")
' map.yaml
)
注意: join
需要文字 Tab
我使用了@Fravadona 的答案所以将其标记为答案
对我的用例进行一些修改后,对我有用的看起来像:
DEFS_PATH="definitions"
declare -A ssmMap
for file in ${DEFS_PATH}/*
do
filename=$(basename -- "$file")
projectName="${filename%.*}"
regex='^([^:]+):[[:space:]]*"(.*)"[[:space:]]*$'
while IFS='' read -r line
do
if [[ $line =~ $regex ]]
then
value="${BASH_REMATCH[2]}"
value=${value//"{{ ssm_env }}"/$INFRA_ENV}
value=${value//"{{ ssm_reg }}"/$SSM_REGION}
value=${value//"{{ projectName }}"/$projectName}
printf -v ssmMap["${BASH_REMATCH[1]}"] '%b' "$value"
else
echo "skipping: $line" 1>&2
fi
done < "$file"
done
基本上在实际用例中,我有一个文件夹,其中包含 yaml 定义。我遍历所有这些以形成关联数组 ssmMap