我在 YAML 文件中有多个配置,我需要使用 Bash 脚本更改一些参数。是否可以?我想避免使用任何外部依赖项。
我的 YAML 看起来像
%YAML 1.2
---
name: mic
components:
- name: Mic
parameters:
period_count: 4
alsa_device_name: "pulse"
---
name: speaker
components:
- name: Speaker
parameters:
period_duration_ms: 20
period_count: 4
alsa_device_name: "pulse"
我希望它的行为就像我提供的那样--mic logitec --speaker hk34
,那么它应该在我的配置中修改alsa_device_name
为mic
speaker
%YAML 1.2
---
name: mic
components:
- name: Mic
parameters:
period_count: 4
alsa_device_name: "logitec"
---
name: speaker
components:
- name: Speaker
parameters:
period_duration_ms: 20
period_count: 4
alsa_device_name: "hk34"
是否可以仅使用 Bash 来完成此操作?如果可以,如何实现?现在我使用的是Python脚本,但这增加了对having的额外依赖python3
,这是我想避免的。
答案1
首先,使用面向行的工具解析像 YAML 这样具有明确定义语法的语言是一个坏主意。不要在生产中使用它!绝不。
有一些语法感知工具可以通过命令行解析 YAML,例如Pythonyq
和去yq
。它们支持 YAML 中的结构,例如锚点、块文字,而标准面向行的工具无法理解这些结构。
对于像上面这样的一次性处理,您可以使用awk
像
BEGIN {
map["name: mic"] = "\"logitec\""
map["name: speaker"] = "\"hk34\""
}
match($0, "name: mic") || match($0, "name: speaker") {
key = substr($0, RSTART, RLENGTH)
}
/alsa_device_name/ {
sub(/:.*/, ": " map[key])
}
{ print }
您可以将上述内容放入awk
脚本 ( .awk
) 中并作为命令行运行awk -f script.awk yaml
或作为命令行的一部分运行。您还可以在命令行中将值定义为
awk -v mic='"logitec"' -v speaker='"hk34"' -f script.awk yaml
并将块中的参数处理BEGIN
为
BEGIN {
map["name: mic"] = mic
map["name: speaker"] = speaker
}
另请注意,您的输入不是有效的 YAML 文件,标题行%YAML 1.2
将使大多数标准 YAML 解析器在您的输入语法上引发错误。
答案2
yq
我一直等到你接受另一个答案才写这个,因为它增加了对from 的依赖https://kislyuk.github.io/yq/
#!/bin/bash
unset mic
unset speaker
while getopts m:s: opt; do
case $opt in
m)
mic=$OPTARG
;;
s)
speaker=$OPTARG
;;
*)
echo 'invalid option' >&2
exit 1
esac
done
shift "$(( OPTIND - 1 ))"
yaml_update () {
key=${1,,} # lower-case, e.g. "mic"
Key=${key^?} # title-case, e.g. "Mic"
value=$2
yq -Y --arg key "$key" --arg Key "$Key" --arg value "$value" '
(select(.name == $key).components[] |
select(.name == $Key).parameters.alsa_device_name) |= $value'
}
if [ "${mic+set}" = "set" ]; then
yaml_update mic "$mic"
else
cat -
fi |
if [ "${speaker+set}" = "set" ]; then
yaml_update speaker "$speaker"
else
cat -
fi
脚本中的大部分代码是命令行解析以及与给定命令行选项相关的逻辑。这是由于问题中的一个要求,或者至少是一个建议,能够在命令行上使用带有一组方便选项的解决方案。实际的代码是做某事位于yaml_update
函数中,由三行代码组成(yq
命令,它只有三行,因为单行代码太长)。
该脚本的使用方式如下:
./script -m logitec -s hk34 <file.yaml >file-new.yaml
它需要两个可选的命令行选项,-m
并且-s
(问题中建议的长选项不是标准的,并且getopts
内置的不支持bash
)来设置麦克风和/或扬声器分别为Alsa设备名称。
YAML 文档在标准输入上读取,并将生成的文档写入标准输出。
更新是通过yq
使用一个表达式来完成的,该表达式根据键选择正确的顶级对象name
,然后components
使用从数组中选择正确的数组元素它是 name
钥匙。然后它更新alsa_device_name
所选元素parameters
键下的值。
为了方便起见,该yq
调用被移至 shell 函数中(我们可能会对 进行两次极其相似的调用yq
,因此这似乎是合理的)。
如果像上面所示更改给定文档,则输出将是
name: mic
components:
- name: Mic
parameters:
period_count: 4
alsa_device_name: "logitec"
---
name: speaker
components:
- name: Speaker
parameters:
period_duration_ms: 20
period_count: 4
alsa_device_name: "hk34"