外部依存関係なしでbashでyamlファイルを変更する

外部依存関係なしでbashでyamlファイルを変更する

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_namemicspeaker

%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スクリプトを使用していますが、これを行うとhaveへの追加の依存関係が追加されるので、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この回答を書く前に、他の回答を受け入れるのを待っていました。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関数に配置され、3行のコードで構成されています(yqコマンド、1行のコードが長すぎて3行しかありません)。

スクリプトは次のように使用されます。

./script -m logitec -s hk34 <file.yaml >file-new.yaml

2つのオプションのコマンドラインオプションが必要です-m-s質問に提案されている長いオプションは標準ではなく、組み込みではサポートさgetoptsれていませんbashマイクそして/またはスピーカーそれぞれ Alsa デバイス名です。

YAML 文書は標準入力として読み取られ、結果文書は標準出力に書き込まれます。

更新は、yqキーに基づいて正しい最上位オブジェクトを選択する式を使用nameし、次を使用してcomponents配列から正しい配列要素を選択することによって行われます。それ nameキー。次に、alsa_device_name選択した要素キーparametersの下の値を更新します。

便宜上、yq呼び出しはシェル関数に移動されました(非常に似た呼び出しを2回実行できるため、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"

関連情報